// GDSL Clearing Service
// Central clearing of sovereign and multi-asset derivatives

import { Decimal } from '@prisma/client/runtime/library';
import { v4 as uuidv4 } from 'uuid';
import { contractFabricService } from '@/core/contracts/contract-fabric.service';
import { Prisma } from '@prisma/client';
import prisma from '@/shared/database/prisma';
import { logger } from '@/infrastructure/monitoring/logger';

export interface DerivativeClearingRequest {
  derivativeType: string; // irs, fx_forward, fx_swap, sovereign_cds, oil_future, gold_swap, agricultural_forward, cbdc_liquidity_future, synthetic_ssu_option, tokenized_bond_future
  party1BankId: string;
  party2BankId: string;
  notionalAmount: string;
  contractTerms: Record<string, unknown>;
  maturityDate?: Date;
}

export interface ClearingResult {
  contractId: string;
  smartContractId?: string;
  status: string;
}

export class GdslClearingService {
  /**
   * Clear derivative contract
   */
  async clearDerivative(
    request: DerivativeClearingRequest
  ): Promise<ClearingResult> {
    const contractId = `DERIV-${uuidv4()}`;

    // Create derivative contract
    const contract = await prisma.derivativeContract.create({
      data: {
        contractId,
        derivativeType: request.derivativeType,
        party1BankId: request.party1BankId,
        party2BankId: request.party2BankId,
        notionalAmount: new Decimal(request.notionalAmount),
        contractTerms: request.contractTerms,
        status: 'active',
        maturityDate: request.maturityDate || null,
      },
    });

    // Create smart contract binding
    let smartContractId: string | undefined;
    try {
      const smartContract = await contractFabricService.createContract(
        request.party1BankId,
        'DERIVATIVE_CLEARING' as any, // Extend ContractTemplateType if needed
        {
          contractId,
          derivativeType: request.derivativeType,
          notionalAmount: request.notionalAmount,
          ...request.contractTerms,
        },
        [request.party1BankId, request.party2BankId]
      );

      smartContractId = smartContract.contractId;

      // Update derivative contract with smart contract reference
      await prisma.derivativeContract.update({
        where: { contractId },
        data: { smartContractId },
      });
    } catch (error) {
      // Smart contract creation is optional, continue without it
      logger.warn('Smart contract creation failed', {
        error: error instanceof Error ? error.message : 'Unknown error',
        contractId,
      });
    }

    return {
      contractId: contract.contractId,
      smartContractId,
      status: contract.status,
    };
  }

  /**
   * Get cleared contracts for a bank
   */
  async getClearedContracts(
    sovereignBankId: string,
    status?: string
  ): Promise<any[]> {
    const where: any = {
      OR: [
        { party1BankId: sovereignBankId },
        { party2BankId: sovereignBankId },
      ],
    };

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

    const contracts = await prisma.derivativeContract.findMany({
      where,
      orderBy: { initiatedAt: 'desc' },
    });

    return contracts;
  }

  /**
   * Terminate derivative contract
   */
  async terminateContract(
    contractId: string,
    reason: string
  ): Promise<void> {
    await prisma.derivativeContract.update({
      where: { contractId },
      data: {
        status: 'terminated',
        contractTerms: {
          terminationReason: reason,
          terminatedAt: new Date().toISOString(),
        },
      },
    });
  }

  /**
   * Get contract exposure
   */
  async getContractExposure(contractId: string): Promise<Decimal> {
    const contract = await prisma.derivativeContract.findUnique({
      where: { contractId },
    });

    if (!contract) {
      throw new Error(`Contract not found: ${contractId}`);
    }

    // In production, this would calculate actual exposure based on market conditions
    // For now, return notional amount as exposure
    return contract.notionalAmount;
  }
}

export const gdslClearingService = new GdslClearingService();

