// Valuation Service
// Real-time fair value calculation

import prisma from '@/shared/database/prisma';
import { Decimal } from '@prisma/client/runtime/library';
import { accountingStandardsService } from './accounting-standards.service';


export class ValuationService {
  /**
   * Calculate real-time fair value for an asset
   */
  async calculateFairValue(assetType: string, assetId: string, currencyCode: string): Promise<number> {
    const rule = await accountingStandardsService.getValuationRule(assetType);

    if (!rule) {
      throw new Error(`No valuation rule found for asset type: ${assetType}`);
    }

    switch (rule.valuationMethod) {
      case 'fair_value':
        return await this.calculateFairValueDirect(assetType, assetId, currencyCode);
      case 'commodity_feed':
        return await this.calculateFromCommodityFeed(assetType, assetId, currencyCode);
      case 'fx_reference_rate':
        return await this.calculateFromFXRate(assetType, assetId, currencyCode);
      default:
        throw new Error(`Unsupported valuation method: ${rule.valuationMethod}`);
    }
  }

  /**
   * Calculate fair value directly (for fiat, CBDC)
   */
  private async calculateFairValueDirect(
    assetType: string,
    assetId: string,
    currencyCode: string
  ): Promise<number> {
    if (assetType === 'fiat' || assetType === 'cbdc') {
      // Fiat and CBDC are already at fair value (1:1)
      const account = await prisma.bankAccount.findUnique({
        where: { id: assetId },
      });

      if (account) {
        return parseFloat(account.balance.toString());
      }
    }

    throw new Error(`Cannot calculate fair value directly for asset type: ${assetType}`);
  }

  /**
   * Calculate from commodity feed
   */
  private async calculateFromCommodityFeed(
    assetType: string,
    assetId: string,
    currencyCode: string
  ): Promise<number> {
    if (assetType !== 'commodity') {
      throw new Error('Commodity feed valuation only applies to commodities');
    }

    // Get commodity
    const commodity = await prisma.commodity.findFirst({
      where: { id: assetId },
    });

    if (!commodity) {
      throw new Error(`Commodity not found: ${assetId}`);
    }

    // Get current price from feed
    const price = await accountingStandardsService.getCommodityFeedPrice(
      commodity.commodityType,
      commodity.unit
    );

    if (!price) {
      throw new Error(`No price feed available for commodity: ${commodity.commodityType}`);
    }

    // Get quantity from account or sub-ledger
    const account = await prisma.bankAccount.findFirst({
      where: {
        assetType: 'commodity',
        currencyCode: commodity.commodityType,
      },
    });

    const quantity = account ? parseFloat(account.balance.toString()) : 0;

    return price * quantity;
  }

  /**
   * Calculate from FX reference rate
   */
  private async calculateFromFXRate(
    assetType: string,
    assetId: string,
    currencyCode: string
  ): Promise<number> {
    // Get account
    const account = await prisma.bankAccount.findUnique({
      where: { id: assetId },
    });

    if (!account) {
      throw new Error(`Account not found: ${assetId}`);
    }

    const baseAmount = parseFloat(account.balance.toString());

    // If account currency matches target currency, no conversion needed
    if (account.currencyCode === currencyCode) {
      return baseAmount;
    }

    // Get FX rate
    const fxRate = await accountingStandardsService.getFXReferenceRate(
      account.currencyCode,
      currencyCode
    );

    if (!fxRate) {
      throw new Error(
        `No FX rate available for ${account.currencyCode}/${currencyCode}`
      );
    }

    return baseAmount * fxRate;
  }

  /**
   * Mark all assets to fair value (batch operation)
   */
  async markAllToFairValue(sovereignBankId?: string) {
    const where: { assetType?: string; sovereignBankId?: string } = {};

    if (sovereignBankId) {
      where.sovereignBankId = sovereignBankId;
    }

    const accounts = await prisma.bankAccount.findMany({
      where,
      include: {
        sovereignBank: true,
      },
    });

    const results = [];

    for (const account of accounts) {
      try {
        const fairValue = await this.calculateFairValue(
          account.assetType,
          account.id,
          account.currencyCode
        );

        await accountingStandardsService.markToFairValue(
          account.id,
          account.assetType,
          fairValue,
          account.currencyCode
        );

        results.push({
          accountId: account.id,
          assetType: account.assetType,
          fairValue,
          success: true,
        });
      } catch (error) {
        results.push({
          accountId: account.id,
          assetType: account.assetType,
          error: error instanceof Error ? error.message : 'Unknown error',
          success: false,
        });
      }
    }

    return results;
  }
}

export const valuationService = new ValuationService();

