// GRU Bonds Service
// GRU-linked bond systems (Li99PpOsB10, Li99PpAvB10)

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 GruBondRequest {
  bondType: 'Li99PpOsB10' | 'Li99PpAvB10';
  principalAmount: string;
  gruUnitId: string;
  maturityYears: number;
  interestRate?: Decimal;
  sovereignBankId: string;
}

export interface GruBondResult {
  bondId: string;
  bondType: string;
  principalAmount: Decimal;
  gruUnitId: string;
  maturityDate: Date;
  interestRate: Decimal;
  status: string;
}

export class GruBondsService {
  /**
   * Issue GRU-linked bond
   * Bond types: Li99PpOsB10, Li99PpAvB10
   */
  async issueGruBond(request: GruBondRequest): Promise<GruBondResult> {
    logger.info('GRU: Issuing GRU-linked bond', { request });

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

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

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

    // Step 3: Determine interest rate based on bond type
    const interestRate =
      request.interestRate || this.getDefaultInterestRate(request.bondType);

    // Step 4: Create bond
    const bondId = `GRU-BOND-${uuidv4()}`;

    const bond = await prisma.gruBond.create({
      data: {
        bondId,
        bondType: request.bondType,
        principalAmount: new Decimal(request.principalAmount),
        gruUnitId: request.gruUnitId,
        sovereignBankId: request.sovereignBankId,
        maturityDate,
        interestRate,
        couponRate: interestRate, // Same as interest rate for simplicity
        status: 'active',
        issuedAt: new Date(),
      },
    });

    logger.info('GRU: Bond issued', { bondId, bondType: request.bondType });

    return {
      bondId,
      bondType: request.bondType,
      principalAmount: bond.principalAmount,
      gruUnitId: request.gruUnitId,
      maturityDate,
      interestRate,
      status: bond.status,
    };
  }

  /**
   * Get default interest rate for bond type
   */
  private getDefaultInterestRate(
    bondType: 'Li99PpOsB10' | 'Li99PpAvB10'
  ): Decimal {
    // Li99PpOsB10: 99-year bond with 10% interest
    // Li99PpAvB10: 99-year bond with 10% average interest
    switch (bondType) {
      case 'Li99PpOsB10':
        return new Decimal(0.1); // 10%
      case 'Li99PpAvB10':
        return new Decimal(0.1); // 10% average
      default:
        return new Decimal(0.05); // Default 5%
    }
  }

  /**
   * Calculate bond value at maturity
   */
  async calculateBondValueAtMaturity(bondId: string): Promise<Decimal> {
    const bond = await prisma.gruBond.findUnique({
      where: { bondId },
    });

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

    // Calculate compound interest: P * (1 + r)^n
    const principal = bond.principalAmount;
    const rate = bond.interestRate;
    const years = this.getYearsUntilMaturity(bond.maturityDate);

    const maturityValue = principal.times(
      new Decimal(1).plus(rate).pow(years)
    );

    return maturityValue;
  }

  /**
   * Get years until maturity
   */
  private getYearsUntilMaturity(maturityDate: Date): number {
    const now = new Date();
    const diffTime = maturityDate.getTime() - now.getTime();
    const diffYears = diffTime / (1000 * 60 * 60 * 24 * 365.25);
    return Math.max(0, diffYears);
  }

  /**
   * Pay bond coupon
   */
  async payBondCoupon(bondId: string): Promise<string> {
    const bond = await prisma.gruBond.findUnique({
      where: { bondId },
    });

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

    // Calculate coupon payment: principal * coupon_rate
    const couponAmount = bond.principalAmount.times(bond.couponRate);

    // Create coupon payment record
    const paymentId = `GRU-COUPON-${uuidv4()}`;

    await prisma.gruBondCoupon.create({
      data: {
        paymentId,
        bondId,
        couponAmount,
        paymentDate: new Date(),
        status: 'paid',
      },
    });

    logger.info('GRU: Bond coupon paid', { bondId, couponAmount: couponAmount.toString() });

    return paymentId;
  }

  /**
   * Redeem bond at maturity
   */
  async redeemBond(bondId: string): Promise<string> {
    const bond = await prisma.gruBond.findUnique({
      where: { bondId },
    });

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

    // Check if bond is at or past maturity
    if (new Date() < bond.maturityDate) {
      throw new Error(`Bond not yet at maturity: ${bondId}`);
    }

    // Calculate final value
    const finalValue = await this.calculateBondValueAtMaturity(bondId);

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

    logger.info('GRU: Bond redeemed', { bondId, finalValue: finalValue.toString() });

    return bondId;
  }

  /**
   * Get bond by ID
   */
  async getBond(bondId: string) {
    return await prisma.gruBond.findUnique({
      where: { bondId },
      include: {
        coupons: true,
      },
    });
  }

  /**
   * Get bonds for a bank
   */
  async getBondsForBank(sovereignBankId: string) {
    return await prisma.gruBond.findMany({
      where: { sovereignBankId },
      orderBy: { issuedAt: 'desc' },
      include: {
        coupons: true,
      },
    });
  }
}

export const gruBondsService = new GruBondsService();

