// GSDS Contract Service
// Synthetic derivative contract creation and management

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

export interface CreateSyntheticDerivativeRequest {
  derivativeType: string; // synthetic_currency, multi_asset, synthetic_credit, behavioral
  party1BankId: string;
  party2BankId?: string;
  underlyingAsset: string; // SSU, CBDC, commodity, security, fiat, FX
  notionalAmount: string;
  contractTerms: Record<string, unknown>;
  maturityDate?: Date;
  marginRequirement?: string;
  sriThreshold?: string;
}

export interface ContractResult {
  derivativeId: string;
  smartContractId?: string;
  status: string;
}

export class GsdsContractService {
  /**
   * Create synthetic derivative contract
   * Smart contract template: if (collateral_value >= margin_requirement && SRI < threshold) activate_contract() else auto_close()
   */
  async createContract(request: CreateSyntheticDerivativeRequest): Promise<ContractResult> {
    const derivativeId = `GSDS-${uuidv4()}`;

    // Get SRI scores for both parties
    const sri1 = await sriCalculatorService.calculateSRI(request.party1BankId);
    const sri1Score = parseFloat(sri1.sriScore.toString());

    let sri2Score = 100; // Default to high risk if no party2
    if (request.party2BankId) {
      const sri2 = await sriCalculatorService.calculateSRI(request.party2BankId);
      sri2Score = parseFloat(sri2.sriScore.toString());
    }

    // Use provided threshold or default
    const sriThreshold = request.sriThreshold ? parseFloat(request.sriThreshold) : 50;

    // Check if SRI exceeds threshold
    const maxSRI = Math.max(sri1Score, sri2Score);
    if (maxSRI >= sriThreshold) {
      throw new Error(`SRI threshold exceeded: ${maxSRI} >= ${sriThreshold}`);
    }

    // Create derivative contract
    const derivative = await prisma.synthetic_derivatives.create({
      data: {
        id: uuidv4(),
        derivativeId,
        derivativeType: request.derivativeType,
        party1BankId: request.party1BankId,
        party2BankId: request.party2BankId || null,
        underlyingAsset: request.underlyingAsset,
        notionalAmount: new Decimal(request.notionalAmount),
        contractTerms: request.contractTerms as Prisma.InputJsonValue,
        status: 'active',
        maturityDate: request.maturityDate || null,
        createdAt: new Date(),
        updatedAt: new Date(),
      },
    });

    // Create smart contract binding
    let smartContractId: string | undefined;
    try {
      const smartContract = await contractFabricService.createContract(
        request.party1BankId,
        'SYNTHETIC_DERIVATIVE' as any,
        {
          derivativeId,
          derivativeType: request.derivativeType,
          notionalAmount: request.notionalAmount,
          marginRequirement: request.marginRequirement || '0',
          sriThreshold: sriThreshold.toString(),
          ...request.contractTerms,
        },
        request.party2BankId ? [request.party1BankId, request.party2BankId] : [request.party1BankId]
      );

      smartContractId = smartContract.contractId;
    } catch (error) {
      logger.warn('Smart contract creation failed', {
        error: error instanceof Error ? error.message : 'Unknown error',
        derivativeId,
      });
    }

    // Calculate initial pricing
    await gsdsPricingService.calculatePrice({
      derivativeId,
      baseValue: request.notionalAmount,
    });

    return {
      derivativeId: derivative.derivativeId,
      smartContractId,
      status: derivative.status,
    };
  }

  /**
   * Activate contract (check collateral and SRI)
   */
  async activateContract(derivativeId: string): Promise<void> {
    const derivative = await prisma.synthetic_derivatives.findUnique({
      where: { derivativeId },
      include: {
        synthetic_derivative_collaterals: {
          where: { status: 'active' },
        },
      },
    });

    if (!derivative) {
      throw new Error(`Derivative not found: ${derivativeId}`);
    }

    // Get margin requirement from contract terms
    const marginRequirement = new Decimal(
      (derivative.contractTerms as any).marginRequirement || '0'
    );
    const sriThreshold = parseFloat(
      (derivative.contractTerms as any).sriThreshold || '50'
    );

    // Calculate total collateral value
    const totalCollateral = derivative.synthetic_derivative_collaterals.reduce(
      (sum, col) => sum.plus(col.valuation),
      new Decimal(0)
    );

    // Get current SRI scores
    const sri1 = await sriCalculatorService.calculateSRI(derivative.party1BankId);
    const sri1Score = parseFloat(sri1.sriScore.toString());

    let sri2Score = 0;
    if (derivative.party2BankId) {
      const sri2 = await sriCalculatorService.calculateSRI(derivative.party2BankId);
      sri2Score = parseFloat(sri2.sriScore.toString());
    }

    const maxSRI = Math.max(sri1Score, sri2Score);

    // Smart contract logic: if (collateral_value >= margin_requirement && SRI < threshold) activate_contract() else auto_close()
    if (totalCollateral.gte(marginRequirement) && maxSRI < sriThreshold) {
      // Activate contract
      await prisma.synthetic_derivatives.update({
        where: { derivativeId },
        data: { status: 'active' },
      });
    } else {
      // Auto-close
      await this.autoClose(derivativeId, {
        reason: totalCollateral.lt(marginRequirement)
          ? 'insufficient_collateral'
          : 'sri_threshold_exceeded',
        collateralValue: totalCollateral.toString(),
        marginRequirement: marginRequirement.toString(),
        sriScore: maxSRI,
        sriThreshold,
      });
    }
  }

  /**
   * Auto-close contract
   */
  async autoClose(derivativeId: string, reason: Record<string, unknown>): Promise<void> {
    const derivative = await prisma.synthetic_derivatives.findUnique({
      where: { derivativeId },
    });
    const existing = derivative?.contractTerms;
    const base =
      typeof existing === 'object' && existing !== null
        ? (existing as Record<string, unknown>)
        : {};

    await prisma.synthetic_derivatives.update({
      where: { derivativeId },
      data: {
        status: 'auto_closed',
        contractTerms: { ...base, autoCloseReason: reason } as Prisma.InputJsonValue,
      },
    });
  }

  /**
   * Get derivative contract
   */
  async getContract(derivativeId: string) {
    return await prisma.synthetic_derivatives.findUnique({
      where: { derivativeId },
      include: {
        gsds_pricing_engine: {
          orderBy: { calculatedAt: 'desc' },
          take: 1,
        },
        synthetic_derivative_collaterals: {
          where: { status: 'active' },
        },
        synthetic_derivative_settlements: {
          orderBy: { createdAt: 'desc' },
        },
      },
    });
  }

  /**
   * List derivatives for a bank
   */
  async listDerivatives(bankId: string, status?: string) {
    return await prisma.synthetic_derivatives.findMany({
      where: {
        OR: [
          { party1BankId: bankId },
          { party2BankId: bankId },
        ],
        ...(status ? { status } : {}),
      },
      include: {
        gsds_pricing_engine: {
          orderBy: { calculatedAt: 'desc' },
          take: 1,
        },
      },
      orderBy: { createdAt: 'desc' },
    });
  }
}

export const gsdsContractService = new GsdsContractService();
