// GRU Legal Instruments Service
// Volume II: Supranational Reserve Framework
// Issues GRC (GRU Reserve Certificate), GRB (GRU Reserve Bond), GSDR (GRU SDR Instrument)

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 { gruSdrService } from './gru-sdr.service';


export interface IssueGrcRequest {
  poolId: string;
  allocationId: string;
  amount: string;
  holderId: string;
  holderType: 'sovereign_bank' | 'supranational_entity';
  currencyCode?: string;
  assetType?: string;
}

export interface IssueGrbRequest {
  poolId?: string;
  entityId?: string;
  principalAmount: string;
  maturityYears: number; // 5-50 years
  interestRate: string;
  couponRate: string;
  couponFrequency?: 'quarterly' | 'semi_annual' | 'annual';
}

export interface IssueGsdrRequest {
  sdrId: string;
  amount: string;
  holderId: string;
  holderType: 'sovereign_bank' | 'supranational_entity';
  purpose?: string;
}

export interface GrcResult {
  certificateId: string;
  certificateCode: string;
  amount: Decimal;
  status: string;
}

export interface GrbResult {
  bondId: string;
  bondCode: string;
  principalAmount: Decimal;
  maturityDate: Date;
  status: string;
}

export interface GsdrResult {
  instrumentId: string;
  sdrAmount: Decimal;
  status: string;
}

export class GruLegalInstrumentsService {
  /**
   * Issue GRU Reserve Certificate (GRC)
   * Identifies supranational reserve allocations
   */
  async issueGrc(request: IssueGrcRequest): Promise<GrcResult> {
    logger.info('GRU Legal Instruments: Issuing GRC', { request });

    // Verify pool exists
    const pool = await prisma.gruReservePool.findUnique({
      where: { poolId: request.poolId },
    });

    if (!pool) {
      throw new Error(`Reserve pool not found: ${request.poolId}`);
    }

    // Verify allocation exists
    const allocation = await prisma.gruReserveAllocation.findUnique({
      where: { allocationId: request.allocationId },
    });

    if (!allocation) {
      throw new Error(`Allocation not found: ${request.allocationId}`);
    }

    const amount = new Decimal(request.amount);
    const certificateId = `GRC-${uuidv4()}`;
    const certificateCode = `GRC-${this.generateCode()}-${this.generateCode()}`;

    const certificate = await prisma.gruReserveCertificate.create({
      data: {
        certificateId,
        certificateCode,
        poolId: request.poolId,
        allocationId: request.allocationId,
        amount,
        currencyCode: request.currencyCode || null,
        assetType: request.assetType || null,
        holderId: request.holderId,
        holderType: request.holderType,
        status: 'active',
        issuedAt: new Date(),
      },
    });

    logger.info('GRU Legal Instruments: GRC issued', {
      certificateId,
      certificateCode,
      amount: amount.toString(),
    });

    return {
      certificateId: certificate.certificateId,
      certificateCode: certificate.certificateCode,
      amount: certificate.amount,
      status: certificate.status,
    };
  }

  /**
   * Issue GRU Reserve Bond (GRB)
   * 5-50 year reserve-linked supranational issuance
   */
  async issueGrb(request: IssueGrbRequest): Promise<GrbResult> {
    logger.info('GRU Legal Instruments: Issuing GRB', { request });

    // Validate maturity years
    if (request.maturityYears < 5 || request.maturityYears > 50) {
      throw new Error(
        `Maturity years must be between 5 and 50, got ${request.maturityYears}`
      );
    }

    const principalAmount = new Decimal(request.principalAmount);
    const interestRate = new Decimal(request.interestRate);
    const couponRate = new Decimal(request.couponRate);

    // Calculate maturity date
    const maturityDate = new Date();
    maturityDate.setFullYear(
      maturityDate.getFullYear() + request.maturityYears
    );

    const bondId = `GRB-${uuidv4()}`;
    const bondCode = `GRB-${this.generateCode()}-${this.generateCode()}`;

    const bond = await prisma.gruReserveBond.create({
      data: {
        bondId,
        bondCode,
        poolId: request.poolId || null,
        entityId: request.entityId || null,
        principalAmount,
        maturityYears: request.maturityYears,
        maturityDate,
        interestRate,
        couponRate,
        couponFrequency: request.couponFrequency || 'annual',
        bondType: 'supranational_reserve_bond',
        status: 'active',
        issuedAt: new Date(),
      },
    });

    // Create first coupon payment schedule
    await this.scheduleCouponPayments(bondId, maturityDate, couponRate, principalAmount, request.couponFrequency || 'annual');

    logger.info('GRU Legal Instruments: GRB issued', {
      bondId,
      bondCode,
      principalAmount: principalAmount.toString(),
      maturityYears: request.maturityYears,
    });

    return {
      bondId: bond.bondId,
      bondCode: bond.bondCode,
      principalAmount: bond.principalAmount,
      maturityDate: bond.maturityDate,
      status: bond.status,
    };
  }

  /**
   * Schedule coupon payments for a bond
   */
  private async scheduleCouponPayments(
    bondId: string,
    maturityDate: Date,
    couponRate: Decimal,
    principalAmount: Decimal,
    frequency: 'quarterly' | 'semi_annual' | 'annual'
  ): Promise<void> {
    const couponAmount = principalAmount.times(couponRate);

    let paymentDate = new Date();
    const payments: Array<{ paymentDate: Date; couponAmount: Decimal }> = [];

    // Calculate number of payments based on frequency
    let paymentsPerYear = 1;
    if (frequency === 'quarterly') paymentsPerYear = 4;
    else if (frequency === 'semi_annual') paymentsPerYear = 2;

    const yearsToMaturity = Math.floor(
      (maturityDate.getTime() - paymentDate.getTime()) /
        (365.25 * 24 * 60 * 60 * 1000)
    );
    const totalPayments = yearsToMaturity * paymentsPerYear;

    // Calculate payment interval
    const monthsPerPayment = 12 / paymentsPerYear;

    for (let i = 0; i < totalPayments; i++) {
      paymentDate = new Date(paymentDate);
      paymentDate.setMonth(paymentDate.getMonth() + monthsPerPayment);

      if (paymentDate <= maturityDate) {
        payments.push({
          paymentDate: new Date(paymentDate),
          couponAmount,
        });
      }
    }

    // Create coupon records
    for (const payment of payments) {
      await prisma.gruReserveBondCoupon.create({
        data: {
          couponId: `GRB-COUPON-${uuidv4()}`,
          bondId,
          couponAmount: payment.couponAmount,
          paymentDate: payment.paymentDate,
          status: 'pending',
        },
      });
    }

    logger.info('GRU Legal Instruments: Coupon payments scheduled', {
      bondId,
      totalPayments: payments.length,
    });
  }

  /**
   * Pay bond coupon
   */
  async payBondCoupon(couponId: string): Promise<void> {
    const coupon = await prisma.gruReserveBondCoupon.findUnique({
      where: { couponId },
      include: { bond: true },
    });

    if (!coupon) {
      throw new Error(`Coupon not found: ${couponId}`);
    }

    if (coupon.status !== 'pending') {
      throw new Error(`Coupon already ${coupon.status}: ${couponId}`);
    }

    // Mark as paid
    await prisma.gruReserveBondCoupon.update({
      where: { couponId },
      data: {
        status: 'paid',
        paidAt: new Date(),
      },
    });

    logger.info('GRU Legal Instruments: Bond coupon paid', {
      couponId,
      amount: coupon.couponAmount.toString(),
    });
  }

  /**
   * Issue GRU SDR Instrument (GSDR)
   * Cross-border liquidity access instrument
   */
  async issueGsdr(request: IssueGsdrRequest): Promise<GsdrResult> {
    logger.info('GRU Legal Instruments: Issuing GSDR', { request });

    // Verify SDR exists
    const sdr = await prisma.gruSdrInstrument.findUnique({
      where: { sdrId: request.sdrId },
    });

    if (!sdr) {
      throw new Error(`SDR instrument not found: ${request.sdrId}`);
    }

    const sdrAmount = new Decimal(request.amount);
    const instrumentId = `GSDR-${uuidv4()}`;

    // Create SDR transaction record
    const transaction = await prisma.gruSdrTransaction.create({
      data: {
        transactionId: instrumentId,
        sdrId: request.sdrId,
        transactionType: 'allocation',
        amount: sdrAmount,
        sourceCurrency: 'SDR_GRU',
        status: 'completed',
        completedAt: new Date(),
      },
    });

    logger.info('GRU Legal Instruments: GSDR issued', {
      instrumentId,
      sdrAmount: sdrAmount.toString(),
      holderId: request.holderId,
    });

    return {
      instrumentId,
      sdrAmount,
      status: transaction.status,
    };
  }

  /**
   * Get GRC by ID
   */
  async getGrc(certificateId: string) {
    return await prisma.gruReserveCertificate.findUnique({
      where: { certificateId },
      include: {
        pool: true,
      },
    });
  }

  /**
   * Get GRB by ID
   */
  async getGrb(bondId: string) {
    return await prisma.gruReserveBond.findUnique({
      where: { bondId },
      include: {
        pool: true,
        coupons: {
          orderBy: { paymentDate: 'asc' },
        },
      },
    });
  }

  /**
   * Get all GRCs for a holder
   */
  async getGrcsForHolder(holderId: string) {
    return await prisma.gruReserveCertificate.findMany({
      where: { holderId },
      include: {
        pool: true,
      },
      orderBy: { issuedAt: 'desc' },
    });
  }

  /**
   * Get all GRBs for an entity
   */
  async getGrbsForEntity(entityId?: string, poolId?: string) {
    const where: any = {};
    if (entityId) where.entityId = entityId;
    if (poolId) where.poolId = poolId;

    return await prisma.gruReserveBond.findMany({
      where,
      include: {
        pool: true,
        coupons: {
          where: { status: 'pending' },
          orderBy: { paymentDate: 'asc' },
        },
      },
      orderBy: { issuedAt: 'desc' },
    });
  }

  /**
   * Redeem GRC
   */
  async redeemGrc(certificateId: string): Promise<void> {
    const certificate = await prisma.gruReserveCertificate.findUnique({
      where: { certificateId },
    });

    if (!certificate) {
      throw new Error(`Certificate not found: ${certificateId}`);
    }

    if (certificate.status !== 'active') {
      throw new Error(`Certificate not active: ${certificateId}`);
    }

    await prisma.gruReserveCertificate.update({
      where: { certificateId },
      data: {
        status: 'redeemed',
        redeemedAt: new Date(),
      },
    });

    logger.info('GRU Legal Instruments: GRC redeemed', { certificateId });
  }

  /**
   * Redeem GRB at maturity
   */
  async redeemGrb(bondId: string): Promise<void> {
    const bond = await prisma.gruReserveBond.findUnique({
      where: { bondId },
      include: { coupons: true },
    });

    if (!bond) {
      throw new Error(`Bond not found: ${bondId}`);
    }

    if (bond.status !== 'active') {
      throw new Error(`Bond not active: ${bondId}`);
    }

    const now = new Date();
    if (bond.maturityDate > now) {
      throw new Error(`Bond not yet matured: ${bondId}`);
    }

    // Pay any outstanding coupons
    const unpaidCoupons = bond.coupons.filter((c) => c.status === 'pending');
    for (const coupon of unpaidCoupons) {
      await this.payBondCoupon(coupon.couponId);
    }

    // Redeem bond
    await prisma.gruReserveBond.update({
      where: { bondId },
      data: {
        status: 'redeemed',
        redeemedAt: new Date(),
      },
    });

    logger.info('GRU Legal Instruments: GRB redeemed', { bondId });
  }

  /**
   * Generate code for certificate/bond codes
   */
  private generateCode(): string {
    return Math.random().toString(36).substring(2, 6).toUpperCase();
  }
}

export const gruLegalInstrumentsService = new GruLegalInstrumentsService();

