// GRU Audit Service
// XAU triangulation audits and index signature consistency checks

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';
import { gruIndexService, GruIndexCode } from './gru-index.service';
import { gruValuationService } from './gru-valuation.service';


export interface AuditResult {
  auditId: string;
  auditType: string;
  auditResult: 'passed' | 'failed' | 'warning';
  auditDetails: Record<string, unknown>;
}

export class GruAuditService {
  /**
   * Perform XAU triangulation audit
   * Verifies that GRU unit can be triangulated through XAU
   */
  async performXauTriangulationAudit(
    issuanceId: string,
    gruUnitId: string,
    metalIndexLink: GruIndexCode
  ): Promise<string> {
    logger.info('GRU Audit: Performing XAU triangulation audit', {
      issuanceId,
      gruUnitId,
      metalIndexLink,
    });

    const auditId = `GRU-AUDIT-${uuidv4()}`;

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

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

      // Get index
      const index = await gruIndexService.getIndex(metalIndexLink);
      if (!index) {
        throw new Error(`Index not found: ${metalIndexLink}`);
      }

      // Perform XAU triangulation
      const valuation = await gruValuationService.calculateGruValuation({
        gruUnitId,
        targetAssetType: 'commodity',
        targetCommodityType: 'GOLD',
      });

      // Verify triangulation is valid
      const xauValue = valuation.xauValue;
      const targetValue = valuation.targetValue;

      // Check if XAU value is within acceptable range
      // For LiXAU, should be approximately 1:1
      let auditPassed = true;
      const auditDetails: Record<string, unknown> = {
        gruUnitId,
        metalIndexLink,
        xauValue: xauValue.toString(),
        targetValue: targetValue.toString(),
        triangulationRate: valuation.triangulationRate.toString(),
      };

      if (metalIndexLink === 'LiXAU') {
        // For LiXAU, expect 1 GRU M00 = 1 oz XAU
        const expectedRatio = new Decimal(1);
        const actualRatio = xauValue.dividedBy(new Decimal(gruUnit.amount));

        // Allow 1% variance
        const variance = actualRatio.minus(expectedRatio).abs();
        if (variance.greaterThan(new Decimal(0.01))) {
          auditPassed = false;
          auditDetails.error = `XAU ratio deviation: ${variance.toString()}`;
        } else {
          auditDetails.ratioMatch = true;
          auditDetails.actualRatio = actualRatio.toString();
        }
      } else {
        // For other indexes, verify triangulation exists
        if (targetValue.isZero() || xauValue.isZero()) {
          auditPassed = false;
          auditDetails.error = 'Triangulation returned zero value';
        } else {
          auditDetails.triangulationVerified = true;
        }
      }

      // Create audit record
      const audit = await prisma.gruIssuanceAudit.create({
        data: {
          auditId,
          issuanceId,
          auditType: 'xau_triangulation',
          auditResult: auditPassed ? 'passed' : 'failed',
          auditDetails,
          auditorId: 'DBIS_AUDIT_SYSTEM',
          nextAuditDate: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000), // Next audit in 90 days
        },
      });

      logger.info('GRU Audit: XAU triangulation audit completed', {
        auditId,
        result: audit.auditResult,
      });

      return auditId;
    } catch (error: any) {
      // Create failed audit record
      const audit = await prisma.gruIssuanceAudit.create({
        data: {
          auditId,
          issuanceId,
          auditType: 'xau_triangulation',
          auditResult: 'failed',
          auditDetails: {
            error: error.message,
          },
          auditorId: 'DBIS_AUDIT_SYSTEM',
        },
      });

      logger.error('GRU Audit: XAU triangulation audit failed', {
        auditId,
        error: error.message,
      });

      return auditId;
    }
  }

  /**
   * Perform index signature consistency check
   * Verifies that GRU index signature is consistent with underlying metal prices
   */
  async performIndexSignatureConsistencyCheck(
    issuanceId: string,
    metalIndexLink: GruIndexCode
  ): Promise<string> {
    logger.info('GRU Audit: Performing index signature consistency check', {
      issuanceId,
      metalIndexLink,
    });

    const auditId = `GRU-AUDIT-${uuidv4()}`;

    try {
      // Get index
      const index = await gruIndexService.getIndex(metalIndexLink);
      if (!index) {
        throw new Error(`Index not found: ${metalIndexLink}`);
      }

      // Get recent price history
      const history = await gruIndexService.getIndexHistory(metalIndexLink, 10);

      // Check for consistency
      const auditDetails: Record<string, unknown> = {
        metalIndexLink,
        currentValue: index.currentValue.toString(),
        baseValue: index.baseValue.toString(),
        historyPoints: history.length,
      };

      let auditPassed = true;

      // Check if current value is reasonable compared to base value
      const changePercent = index.changePercent || new Decimal(0);
      if (changePercent.abs().greaterThan(new Decimal(50))) {
        // More than 50% change from base
        auditPassed = false;
        auditDetails.warning = 'Significant deviation from base value';
        auditDetails.changePercent = changePercent.toString();
      }

      // Check for volatility spikes
      if (history.length >= 2) {
        const recentChanges = [];
        for (let i = 1; i < Math.min(history.length, 5); i++) {
          const prev = history[i - 1].indexValue;
          const curr = history[i].indexValue;
          const change = curr.minus(prev).dividedBy(prev).abs();
          recentChanges.push(change);
        }

        const avgChange = recentChanges.reduce((sum, c) => sum.plus(c), new Decimal(0))
          .dividedBy(recentChanges.length);

        if (avgChange.greaterThan(new Decimal(0.1))) {
          // More than 10% average change
          auditDetails.warning = 'High volatility detected';
          auditDetails.avgChangePercent = avgChange.times(100).toString();
        }

        auditDetails.avgChangePercent = avgChange.times(100).toString();
      }

      // Create audit record
      const audit = await prisma.gruIssuanceAudit.create({
        data: {
          auditId,
          issuanceId,
          auditType: 'index_signature',
          auditResult: auditPassed ? 'passed' : 'warning',
          auditDetails,
          auditorId: 'DBIS_AUDIT_SYSTEM',
          nextAuditDate: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), // Next audit in 30 days
        },
      });

      logger.info('GRU Audit: Index signature consistency check completed', {
        auditId,
        result: audit.auditResult,
      });

      return auditId;
    } catch (error: any) {
      // Create failed audit record
      const audit = await prisma.gruIssuanceAudit.create({
        data: {
          auditId,
          issuanceId,
          auditType: 'index_signature',
          auditResult: 'failed',
          auditDetails: {
            error: error.message,
          },
          auditorId: 'DBIS_AUDIT_SYSTEM',
        },
      });

      logger.error('GRU Audit: Index signature consistency check failed', {
        auditId,
        error: error.message,
      });

      return auditId;
    }
  }

  /**
   * Perform metal link verification
   * Verifies that metal-index link is provable and consistent
   */
  async performMetalLinkVerification(
    issuanceId: string,
    metalIndexLink: GruIndexCode
  ): Promise<string> {
    logger.info('GRU Audit: Performing metal link verification', {
      issuanceId,
      metalIndexLink,
    });

    const auditId = `GRU-AUDIT-${uuidv4()}`;

    try {
      // Get index
      const index = await gruIndexService.getIndex(metalIndexLink);
      if (!index) {
        throw new Error(`Index not found: ${metalIndexLink}`);
      }

      // Verify index calculation method matches expected metal link
      const auditDetails: Record<string, unknown> = {
        metalIndexLink,
        calculationMethod: index.calculationMethod,
        indexValue: index.currentValue.toString(),
      };

      let auditPassed = true;

      // Verify calculation method matches index code
      const expectedMethods: Record<string, string> = {
        LiXAU: 'xau_based',
        LiPMG: 'pgm_based',
        LiBMG1: 'bmg_weighted',
        LiBMG2: 'bmg_weighted',
        LiBMG3: 'bmg_weighted',
      };

      const expectedMethod = expectedMethods[metalIndexLink];
      if (index.calculationMethod !== expectedMethod) {
        auditPassed = false;
        auditDetails.error = `Calculation method mismatch: expected ${expectedMethod}, got ${index.calculationMethod}`;
      } else {
        auditDetails.methodMatch = true;
      }

      // Verify weightings for composite indexes
      if (index.weightings) {
        auditDetails.weightings = index.weightings;
        const weights = index.weightings as Record<string, number>;
        const totalWeight = Object.values(weights).reduce((sum, w) => sum + w, 0);

        // Weights should sum to approximately 1
        if (Math.abs(totalWeight - 1) > 0.01) {
          auditDetails.warning = `Weightings sum to ${totalWeight}, expected 1.0`;
        }
      }

      // Create audit record
      const audit = await prisma.gruIssuanceAudit.create({
        data: {
          auditId,
          issuanceId,
          auditType: 'metal_link_verification',
          auditResult: auditPassed ? 'passed' : 'failed',
          auditDetails,
          auditorId: 'DBIS_AUDIT_SYSTEM',
          nextAuditDate: new Date(Date.now() + 180 * 24 * 60 * 60 * 1000), // Next audit in 180 days
        },
      });

      logger.info('GRU Audit: Metal link verification completed', {
        auditId,
        result: audit.auditResult,
      });

      return auditId;
    } catch (error: any) {
      // Create failed audit record
      const audit = await prisma.gruIssuanceAudit.create({
        data: {
          auditId,
          issuanceId,
          auditType: 'metal_link_verification',
          auditResult: 'failed',
          auditDetails: {
            error: error.message,
          },
          auditorId: 'DBIS_AUDIT_SYSTEM',
        },
      });

      logger.error('GRU Audit: Metal link verification failed', {
        auditId,
        error: error.message,
      });

      return auditId;
    }
  }

  /**
   * Get audit by ID
   */
  async getAudit(auditId: string) {
    return await prisma.gruIssuanceAudit.findUnique({
      where: { auditId },
      include: {
        issuance: true,
      },
    });
  }

  /**
   * Get audits for issuance
   */
  async getAuditsForIssuance(issuanceId: string) {
    return await prisma.gruIssuanceAudit.findMany({
      where: { issuanceId },
      orderBy: { auditDate: 'desc' },
    });
  }
}

export const gruAuditService = new GruAuditService();

