// GRU Issuance Service
// Legal issuance management with Class I-IV classifications

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 { gruLegalRegistrationService } from './gru-legal-registration.service';
import { gruAuditService } from './gru-audit.service';
import { GruIndexCode } from './gru-index.service';


export type IssuanceClass = 'Class_I' | 'Class_II' | 'Class_III' | 'Class_IV';

export interface GruIssuanceRequest {
  sovereignBankId: string;
  issuanceClass: IssuanceClass;
  amount: string;
  unitType: 'M00' | 'M0' | 'M1';
  metalIndexLink: GruIndexCode;
  gruUnitId?: string; // Optional if creating new GRU unit
  registrarOfficeId: string;
  regulatoryClass?: 'SR-1' | 'SR-2' | 'SR-3' | 'M0' | 'M1'; // From GRU Institutional Whitepaper
}

export interface GruIssuanceResult {
  issuanceId: string;
  issuanceClass: string;
  amount: Decimal;
  unitType: string;
  status: string;
  isin?: string;
  cusip?: string;
  qtid?: string;
}

export class GruIssuanceService {
  /**
   * Issue GRU with legal classification
   * Class I: Sovereign-Grade Reserve Instrument (SCBs only)
   * Class II: Institutional-Grade GRU
   * Class III: Restricted Commercial GRU
   * Class IV: Observational GRU (non-settlement)
   */
  async issueGru(request: GruIssuanceRequest): Promise<GruIssuanceResult> {
    logger.info('GRU Issuance: Creating GRU issuance', { request });

    // Validate issuance class requirements
    this.validateIssuanceClass(request.issuanceClass, request.sovereignBankId);

    // Verify metal-index link exists
    const index = await prisma.gruIndex.findUnique({
      where: { indexCode: request.metalIndexLink },
    });

    if (!index) {
      throw new Error(`Index not found: ${request.metalIndexLink}`);
    }

    // Create or use existing GRU unit
    let gruUnitId = request.gruUnitId;
    if (!gruUnitId) {
      const gruUnit = await prisma.gruUnit.create({
        data: {
          gruUnitId: `GRU-UNIT-${uuidv4()}`,
          sovereignBankId: request.sovereignBankId,
          unitType: request.unitType,
          amount: new Decimal(request.amount),
          status: 'active',
        },
      });
      gruUnitId = gruUnit.gruUnitId;
    }

    // Create issuance
    const issuanceId = `GRU-ISS-${uuidv4()}`;

    const issuance = await prisma.gruIssuance.create({
      data: {
        issuanceId,
        gruUnitId,
        sovereignBankId: request.sovereignBankId,
        issuanceClass: request.issuanceClass,
        issuanceType: this.getIssuanceType(request.issuanceClass),
        amount: new Decimal(request.amount),
        unitType: request.unitType,
        metalIndexLink: request.metalIndexLink,
        indexSignatureConsistency: false, // Will be set after audit
        registrarOfficeId: request.registrarOfficeId,
        regulatoryClass: request.regulatoryClass || null, // From GRU Institutional Whitepaper
        status: 'pending',
      },
    });

    // Perform XAU triangulation audit
    const auditId = await gruAuditService.performXauTriangulationAudit(
      issuanceId,
      gruUnitId,
      request.metalIndexLink
    );

    // Update issuance with audit ID
    await prisma.gruIssuance.update({
      where: { issuanceId },
      data: {
        xauTriangulationAuditId: auditId,
      },
    });

    // Register with ISIN, CUSIP, QTID
    const isin = await gruLegalRegistrationService.registerISIN(
      issuanceId,
      request.issuanceClass
    );
    const cusip = await gruLegalRegistrationService.registerCUSIP(
      issuanceId,
      request.issuanceClass
    );
    const qtid = await gruLegalRegistrationService.registerQTID(issuanceId);

    // Approve issuance if all checks pass
    await this.approveIssuance(issuanceId);

    logger.info('GRU Issuance: GRU issued', {
      issuanceId,
      issuanceClass: request.issuanceClass,
      isin,
      cusip,
      qtid,
    });

    return {
      issuanceId,
      issuanceClass: request.issuanceClass,
      amount: new Decimal(request.amount),
      unitType: request.unitType,
      status: 'issued',
      isin,
      cusip,
      qtid,
    };
  }

  /**
   * Validate issuance class requirements
   */
  private validateIssuanceClass(
    issuanceClass: IssuanceClass,
    sovereignBankId: string
  ): void {
    if (issuanceClass === 'Class_I') {
      // Class I: Only SCBs (Sovereign Central Banks)
      // In production, would check if bank is an SCB
      // For now, just log a warning
      logger.warn('GRU Issuance: Class I issuance requires SCB status', {
        sovereignBankId,
      });
    }
  }

  /**
   * Get issuance type from class
   */
  private getIssuanceType(issuanceClass: IssuanceClass): string {
    const types: Record<IssuanceClass, string> = {
      Class_I: 'sovereign_grade',
      Class_II: 'institutional_grade',
      Class_III: 'commercial',
      Class_IV: 'observational',
    };

    return types[issuanceClass];
  }

  /**
   * Approve issuance
   */
  async approveIssuance(issuanceId: string): Promise<void> {
    const issuance = await prisma.gruIssuance.findUnique({
      where: { issuanceId },
      include: {
        audits: true,
        legalRegistrations: true,
      },
    });

    if (!issuance) {
      throw new Error(`Issuance not found: ${issuanceId}`);
    }

    // Check all requirements
    if (!issuance.xauTriangulationAuditId) {
      throw new Error('XAU triangulation audit required');
    }

    // Check audit results
    const audits = issuance.audits;
    const failedAudits = audits.filter((a) => a.auditResult === 'failed');
    if (failedAudits.length > 0) {
      throw new Error(
        `Issuance cannot be approved: ${failedAudits.length} audit(s) failed`
      );
    }

    // Check registrations
    const registrations = issuance.legalRegistrations;
    const requiredTypes = ['ISIN', 'CUSIP', 'QTID'];
    const existingTypes = registrations.map((r) => r.registrationType);
    const missingTypes = requiredTypes.filter((t) => !existingTypes.includes(t));

    if (missingTypes.length > 0) {
      throw new Error(
        `Missing registrations: ${missingTypes.join(', ')}`
      );
    }

    // Approve issuance
    await prisma.gruIssuance.update({
      where: { issuanceId },
      data: {
        status: 'approved',
        approvedAt: new Date(),
        indexSignatureConsistency: true,
      },
    });

    // Issue GRU (activate if not already active)
    await prisma.gruIssuance.update({
      where: { issuanceId },
      data: {
        status: 'issued',
        issuedAt: new Date(),
      },
    });

    logger.info('GRU Issuance: Issuance approved and issued', { issuanceId });
  }

  /**
   * Get issuance by ID
   */
  async getIssuance(issuanceId: string) {
    return await prisma.gruIssuance.findUnique({
      where: { issuanceId },
      include: {
        legalRegistrations: true,
        audits: {
          orderBy: { auditDate: 'desc' },
        },
      },
    });
  }

  /**
   * Get issuances for a bank
   */
  async getIssuancesForBank(
    sovereignBankId: string,
    issuanceClass?: IssuanceClass,
    status?: string
  ) {
    const where: any = { sovereignBankId };
    if (issuanceClass) {
      where.issuanceClass = issuanceClass;
    }
    if (status) {
      where.status = status;
    }

    return await prisma.gruIssuance.findMany({
      where,
      include: {
        legalRegistrations: true,
      },
      orderBy: { createdAt: 'desc' },
    });
  }

  /**
   * Suspend issuance
   */
  async suspendIssuance(issuanceId: string, reason: string): Promise<void> {
    await prisma.gruIssuance.update({
      where: { issuanceId },
      data: {
        status: 'suspended',
      },
    });

    logger.warn('GRU Issuance: Issuance suspended', { issuanceId, reason });
  }

  /**
   * Revoke issuance
   */
  async revokeIssuance(issuanceId: string, reason: string): Promise<void> {
    await prisma.gruIssuance.update({
      where: { issuanceId },
      data: {
        status: 'revoked',
      },
    });

    // Revoke all legal registrations
    await prisma.gruLegalRegistration.updateMany({
      where: { issuanceId },
      data: {
        status: 'revoked',
      },
    });

    logger.warn('GRU Issuance: Issuance revoked', { issuanceId, reason });
  }
}

export const gruIssuanceService = new GruIssuanceService();

