// GRU Valuation Service
// XAU-anchored triangulation to fiat, commodity, CBDC, tokenized assets

import prisma from '@/shared/database/prisma';
import { Decimal } from '@prisma/client/runtime/library';
import { v4 as uuidv4 } from 'uuid';
import { logger } from '@/infrastructure/monitoring/logger';


export interface GruValuationRequest {
  gruUnitId: string;
  targetAssetType: 'fiat' | 'commodity' | 'cbdc' | 'tokenized';
  targetCurrencyCode?: string;
  targetCommodityType?: string;
}

export interface GruValuationResult {
  gruUnitId: string;
  gruAmount: Decimal;
  gruType: string;
  xauValue: Decimal; // Value in XAU (gold)
  targetValue: Decimal;
  targetAssetType: string;
  targetCurrencyCode?: string;
  triangulationRate: Decimal;
}

export class GruValuationService {
  /**
   * Calculate GRU valuation through XAU triangulation
   * All triangulations occur through XAU, anchoring GRU to:
   * - Fiat
   * - Commodity
   * - CBDC
   * - Tokenized assets
   */
  async calculateGruValuation(
    request: GruValuationRequest
  ): Promise<GruValuationResult> {
    logger.info('GRU: Calculating valuation through XAU triangulation', {
      request,
    });

    // Step 1: Get GRU unit
    const gruUnit = await prisma.gruUnit.findUnique({
      where: { gruUnitId: request.gruUnitId },
    });

    if (!gruUnit) {
      throw new Error(`GRU unit not found: ${request.gruUnitId}`);
    }

    // Step 2: Get XAU (gold) price
    const xauPrice = await this.getXauPrice();

    // Step 3: Calculate GRU value in XAU
    // GRU is anchored to XAU at a fixed rate (e.g., 1 GRU M00 = 1 oz XAU)
    const gruAmount = new Decimal(gruUnit.amount);
    const xauValue = this.calculateGruToXau(gruAmount, gruUnit.unitType);

    // Step 4: Triangulate to target asset type
    let targetValue: Decimal;
    let triangulationRate: Decimal;

    switch (request.targetAssetType) {
      case 'fiat':
        targetValue = await this.triangulateToFiat(
          xauValue,
          request.targetCurrencyCode || 'USD'
        );
        triangulationRate = targetValue.dividedBy(gruAmount);
        break;

      case 'commodity':
        targetValue = await this.triangulateToCommodity(
          xauValue,
          request.targetCommodityType || 'SILVER'
        );
        triangulationRate = targetValue.dividedBy(gruAmount);
        break;

      case 'cbdc':
        targetValue = await this.triangulateToCbdc(
          xauValue,
          request.targetCurrencyCode || 'USD'
        );
        triangulationRate = targetValue.dividedBy(gruAmount);
        break;

      case 'tokenized':
        targetValue = await this.triangulateToTokenized(
          xauValue,
          request.targetCurrencyCode || 'USD'
        );
        triangulationRate = targetValue.dividedBy(gruAmount);
        break;

      default:
        throw new Error(`Unsupported target asset type: ${request.targetAssetType}`);
    }

    // Step 5: Save triangulation record
    const triangulationId = await this.saveTriangulation({
      gruUnitId: request.gruUnitId,
      gruAmount,
      gruType: gruUnit.unitType,
      xauValue,
      targetValue,
      targetAssetType: request.targetAssetType,
      targetCurrencyCode: request.targetCurrencyCode,
      targetCommodityType: request.targetCommodityType,
      triangulationRate,
    });

    logger.info('GRU: Valuation calculated', {
      gruUnitId: request.gruUnitId,
      xauValue: xauValue.toString(),
      targetValue: targetValue.toString(),
    });

    return {
      gruUnitId: request.gruUnitId,
      gruAmount,
      gruType: gruUnit.unitType,
      xauValue,
      targetValue,
      targetAssetType: request.targetAssetType,
      targetCurrencyCode: request.targetCurrencyCode,
      triangulationRate,
    };
  }

  /**
   * Calculate GRU to XAU conversion
   * Base rate: 1 GRU M00 = 1 oz XAU (troy ounce)
   */
  private calculateGruToXau(
    gruAmount: Decimal,
    unitType: string
  ): Decimal {
    // Convert to M00 first
    let m00Amount: Decimal;
    switch (unitType) {
      case 'M00':
        m00Amount = gruAmount;
        break;
      case 'M0':
        m00Amount = gruAmount.dividedBy(5); // 1 M00 = 5 M0
        break;
      case 'M1':
        m00Amount = gruAmount.dividedBy(25); // 1 M00 = 25 M1
        break;
      default:
        throw new Error(`Invalid unit type: ${unitType}`);
    }

    // 1 M00 GRU = 1 oz XAU
    return m00Amount;
  }

  /**
   * Get XAU (gold) price
   */
  private async getXauPrice(): Promise<Decimal> {
    // In production, this would fetch from a commodity feed
    // For now, use a default rate (e.g., $2000 per oz)
    const defaultXauPrice = new Decimal(2000);

    // Try to get from commodity feed
    try {
      const commodity = await prisma.commodityDigitalToken.findFirst({
        where: {
          commodityType: 'GOLD',
          status: 'active',
        },
        orderBy: { createdAt: 'desc' },
      });

      if (commodity) {
        // Use commodity price if available
        // In production, would fetch real-time price
        return defaultXauPrice;
      }
    } catch (error) {
      logger.warn('GRU: Could not fetch XAU price from commodity feed', {
        error,
      });
    }

    return defaultXauPrice;
  }

  /**
   * Triangulate XAU to fiat
   */
  private async triangulateToFiat(
    xauValue: Decimal,
    currencyCode: string
  ): Promise<Decimal> {
    // Get XAU price in target currency
    const xauPrice = await this.getXauPrice();

    // Calculate: XAU value * XAU price = fiat value
    return xauValue.times(xauPrice);
  }

  /**
   * Triangulate XAU to commodity
   */
  private async triangulateToCommodity(
    xauValue: Decimal,
    commodityType: string
  ): Promise<Decimal> {
    // Get commodity price relative to XAU
    // For example, if SILVER is 1/80th of GOLD price
    const commodityRatios: Record<string, Decimal> = {
      SILVER: new Decimal(80), // 1 oz gold = 80 oz silver
      PLATINUM: new Decimal(1.2), // 1 oz gold = 1.2 oz platinum
      OIL: new Decimal(0.001), // Example ratio
    };

    const ratio = commodityRatios[commodityType] || new Decimal(1);
    return xauValue.times(ratio);
  }

  /**
   * Triangulate XAU to CBDC
   */
  private async triangulateToCbdc(
    xauValue: Decimal,
    currencyCode: string
  ): Promise<Decimal> {
    // CBDC is typically 1:1 with fiat
    return await this.triangulateToFiat(xauValue, currencyCode);
  }

  /**
   * Triangulate XAU to tokenized assets
   */
  private async triangulateToTokenized(
    xauValue: Decimal,
    currencyCode: string
  ): Promise<Decimal> {
    // Tokenized assets typically follow fiat pricing
    return await this.triangulateToFiat(xauValue, currencyCode);
  }

  /**
   * Save triangulation record
   */
  private async saveTriangulation(data: {
    gruUnitId: string;
    gruAmount: Decimal;
    gruType: string;
    xauValue: Decimal;
    targetValue: Decimal;
    targetAssetType: string;
    targetCurrencyCode?: string;
    targetCommodityType?: string;
    triangulationRate: Decimal;
  }): Promise<string> {
    const triangulationId = `GRU-TRI-${uuidv4()}`;

    await prisma.gruTriangulation.create({
      data: {
        triangulationId,
        gruUnitId: data.gruUnitId,
        gruAmount: data.gruAmount,
        gruType: data.gruType,
        xauValue: data.xauValue,
        targetValue: data.targetValue,
        targetAssetType: data.targetAssetType,
        targetCurrencyCode: data.targetCurrencyCode || null,
        targetCommodityType: data.targetCommodityType || null,
        triangulationRate: data.triangulationRate,
        status: 'completed',
      },
    });

    return triangulationId;
  }

  /**
   * Get triangulation history
   */
  async getTriangulationHistory(gruUnitId?: string, limit: number = 100) {
    return await prisma.gruTriangulation.findMany({
      where: gruUnitId ? { gruUnitId } : {},
      orderBy: { createdAt: 'desc' },
      take: limit,
    });
  }
}

export const gruValuationService = new GruValuationService();

