// GRU Reserve Pool Service
// Volume II: Supranational Reserve Framework
// Manages global, regional, and strategic reserve pools

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 type ReservePoolType = 'global' | 'regional' | 'strategic';

export interface CreateReservePoolRequest {
  poolType: ReservePoolType;
  poolName: string;
  entityId?: string;
  currencyCode?: string;
  assetType?: string;
}

export interface ReservePoolResult {
  poolId: string;
  poolType: string;
  poolName: string;
  totalReserves: Decimal;
  availableReserves: Decimal;
  reservedReserves: Decimal;
  status: string;
}

export interface AllocateReserveRequest {
  poolId: string;
  reserveClassId: string;
  amount: string;
  sovereignBankId?: string;
  entityId?: string;
  allocationType?: string;
  currencyCode?: string;
  assetType?: string;
}

export interface WithdrawReserveRequest {
  poolId: string;
  amount: string;
  sovereignBankId?: string;
  entityId?: string;
  withdrawalType?: string;
  currencyCode?: string;
}

export class GruReservePoolService {
  /**
   * Create a new reserve pool
   */
  async createReservePool(
    request: CreateReservePoolRequest
  ): Promise<ReservePoolResult> {
    logger.info('GRU Reserve Pool: Creating reserve pool', { request });

    const poolId = `GRU-POOL-${uuidv4()}`;

    const pool = await prisma.gru_reserve_pools.create({
      data: {
        id: uuidv4(),
        poolId,
        poolType: request.poolType,
        poolName: request.poolName,
        entityId: request.entityId || null,
        currencyCode: request.currencyCode || null,
        assetType: request.assetType || 'multi_asset',
        totalReserves: new Decimal(0),
        availableReserves: new Decimal(0),
        reservedReserves: new Decimal(0),
        status: 'active',
        createdAt: new Date(),
        updatedAt: new Date(),
      },
    });

    logger.info('GRU Reserve Pool: Pool created', { poolId });

    return {
      poolId: pool.poolId,
      poolType: pool.poolType,
      poolName: pool.poolName,
      totalReserves: pool.totalReserves,
      availableReserves: pool.availableReserves,
      reservedReserves: pool.reservedReserves,
      status: pool.status,
    };
  }

  /**
   * Get reserve pool by ID
   */
  async getReservePool(poolId: string) {
    return await prisma.gru_reserve_pools.findUnique({
      where: { poolId },
      include: {
        entity: true,
        allocations: {
          orderBy: { createdAt: 'desc' },
          take: 10,
        },
        withdrawals: {
          orderBy: { createdAt: 'desc' },
          take: 10,
        },
      },
    });
  }

  /**
   * Get all reserve pools
   */
  async getAllReservePools(
    poolType?: ReservePoolType,
    entityId?: string
  ) {
    const where: any = {};
    if (poolType) where.poolType = poolType;
    if (entityId) where.entityId = entityId;

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

  /**
   * Allocate reserves to a pool
   */
  async allocateReserves(
    request: AllocateReserveRequest
  ): Promise<string> {
    logger.info('GRU Reserve Pool: Allocating reserves', { request });

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

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

    // Verify reserve class exists
    const reserveClass = await prisma.gru_supranational_reserve_classes.findUnique({
      where: { reserveClassId: request.reserveClassId },
    });

    if (!reserveClass) {
      throw new Error(`Reserve class not found: ${request.reserveClassId}`);
    }

    const amount = new Decimal(request.amount);

    // Create allocation record
    const allocationId = `GRU-ALLOC-${uuidv4()}`;

    const allocation = await prisma.gru_reserve_allocations.create({
      data: {
        allocationId,
        poolId: request.poolId,
        reserveClassId: request.reserveClassId,
        sovereignBankId: request.sovereignBankId || null,
        entityId: request.entityId || null,
        amount,
        currencyCode: request.currencyCode || null,
        assetType: request.assetType || null,
        allocationType: request.allocationType || 'initial_allocation',
        status: 'pending',
      },
    });

    // Update pool reserves
    await prisma.gru_reserve_pools.update({
      where: { poolId: request.poolId },
      data: {
        totalReserves: pool.totalReserves.plus(amount),
        availableReserves: pool.availableReserves.plus(amount),
        lastUpdated: new Date(),
      },
    });

    // Confirm allocation
    await prisma.gru_reserve_allocations.update({
      where: { allocationId },
      data: {
        status: 'confirmed',
        confirmedAt: new Date(),
      },
    });

    logger.info('GRU Reserve Pool: Reserves allocated', {
      allocationId,
      poolId: request.poolId,
      amount: amount.toString(),
    });

    return allocationId;
  }

  /**
   * Withdraw reserves from a pool
   */
  async withdrawReserves(
    request: WithdrawReserveRequest
  ): Promise<string> {
    logger.info('GRU Reserve Pool: Withdrawing reserves', { request });

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

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

    const amount = new Decimal(request.amount);

    // Check available reserves
    if (pool.availableReserves.lt(amount)) {
      throw new Error(
        `Insufficient available reserves. Available: ${pool.availableReserves.toString()}, Requested: ${amount.toString()}`
      );
    }

    // Create withdrawal record
    const withdrawalId = `GRU-WDRAW-${uuidv4()}`;

    const withdrawal = await prisma.gru_reserve_withdrawals.create({
      data: {
        withdrawalId,
        poolId: request.poolId,
        sovereignBankId: request.sovereignBankId || null,
        entityId: request.entityId || null,
        amount,
        currencyCode: request.currencyCode || null,
        withdrawalType: request.withdrawalType || 'liquidity_access',
        approvalStatus: 'pending',
      },
    });

    logger.info('GRU Reserve Pool: Withdrawal request created', {
      withdrawalId,
      poolId: request.poolId,
      amount: amount.toString(),
    });

    return withdrawalId;
  }

  /**
   * Approve withdrawal
   */
  async approveWithdrawal(
    withdrawalId: string,
    approverId?: string
  ): Promise<void> {
    const withdrawal = await prisma.gru_reserve_withdrawals.findUnique({
      where: { withdrawalId },
      include: { pool: true },
    });

    if (!withdrawal) {
      throw new Error(`Withdrawal not found: ${withdrawalId}`);
    }

    if (withdrawal.approvalStatus !== 'pending') {
      throw new Error(
        `Withdrawal already ${withdrawal.approvalStatus}: ${withdrawalId}`
      );
    }

    // Check available reserves
    if (withdrawal.defi_liquidity_pools.availableReserves.lt(withdrawal.amount)) {
      throw new Error('Insufficient available reserves for withdrawal');
    }

    // Approve withdrawal
    await prisma.gru_reserve_withdrawals.update({
      where: { withdrawalId },
      data: {
        approvalStatus: 'approved',
        approvedAt: new Date(),
      },
    });

    // Execute withdrawal - update pool
    await prisma.gru_reserve_pools.update({
      where: { poolId: withdrawal.poolId },
      data: {
        availableReserves: withdrawal.defi_liquidity_pools.availableReserves.minus(
          withdrawal.amount
        ),
        reservedReserves: withdrawal.defi_liquidity_pools.reservedReserves.plus(
          withdrawal.amount
        ),
        lastUpdated: new Date(),
      },
    });

    // Mark as executed
    await prisma.gru_reserve_withdrawals.update({
      where: { withdrawalId },
      data: {
        approvalStatus: 'executed',
        executedAt: new Date(),
      },
    });

    logger.info('GRU Reserve Pool: Withdrawal approved and executed', {
      withdrawalId,
      amount: withdrawal.amount.toString(),
    });
  }

  /**
   * Handle liquidity mutualization
   * Transfers liquidity between pools or entities
   */
  async mutualizeLiquidity(
    sourcePoolId: string,
    targetPoolId: string,
    amount: string
  ): Promise<string> {
    logger.info('GRU Reserve Pool: Mutualizing liquidity', {
      sourcePoolId,
      targetPoolId,
      amount,
    });

    const sourcePool = await prisma.gru_reserve_pools.findUnique({
      where: { poolId: sourcePoolId },
    });

    const targetPool = await prisma.gru_reserve_pools.findUnique({
      where: { poolId: targetPoolId },
    });

    if (!sourcePool || !targetPool) {
      throw new Error('Source or target pool not found');
    }

    const transferAmount = new Decimal(amount);

    if (sourcePool.availableReserves.lt(transferAmount)) {
      throw new Error('Insufficient reserves in source pool');
    }

    // Update source pool
    await prisma.gru_reserve_pools.update({
      where: { poolId: sourcePoolId },
      data: {
        totalReserves: sourcePool.totalReserves.minus(transferAmount),
        availableReserves: sourcePool.availableReserves.minus(transferAmount),
        lastUpdated: new Date(),
      },
    });

    // Update target pool
    await prisma.gru_reserve_pools.update({
      where: { poolId: targetPoolId },
      data: {
        totalReserves: targetPool.totalReserves.plus(transferAmount),
        availableReserves: targetPool.availableReserves.plus(transferAmount),
        lastUpdated: new Date(),
      },
    });

    logger.info('GRU Reserve Pool: Liquidity mutualized', {
      sourcePoolId,
      targetPoolId,
      amount: transferAmount.toString(),
    });

    return `MUTUAL-${uuidv4()}`;
  }

  /**
   * Regional crisis intervention
   * Rapid allocation for crisis response
   */
  async crisisIntervention(
    poolId: string,
    amount: string,
    crisisType: string
  ): Promise<string> {
    logger.warn('GRU Reserve Pool: Crisis intervention initiated', {
      poolId,
      amount,
      crisisType,
    });

    const pool = await prisma.gru_reserve_pools.findUnique({
      where: { poolId },
    });

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

    const interventionAmount = new Decimal(amount);

    // Create emergency allocation
    const allocationId = await this.allocateReserves({
      poolId,
      reserveClassId: 'SR-2', // Regional Reserve GRU for crisis
      amount: interventionAmount.toString(),
      allocationType: 'crisis_intervention',
    });

    logger.warn('GRU Reserve Pool: Crisis intervention completed', {
      poolId,
      allocationId,
      amount: interventionAmount.toString(),
    });

    return allocationId;
  }

  /**
   * FX corridor smoothing
   * Stabilize FX rates using reserve pool
   */
  async smoothFxCorridor(
    poolId: string,
    currencyPair: string,
    targetRate: string,
    interventionAmount: string
  ): Promise<string> {
    logger.info('GRU Reserve Pool: FX corridor smoothing', {
      poolId,
      currencyPair,
      targetRate,
      interventionAmount,
    });

    // This would integrate with FX service in production
    // For now, create a withdrawal for FX intervention
    const withdrawalId = await this.withdrawReserves({
      poolId,
      amount: interventionAmount,
      withdrawalType: 'fx_stabilization',
      currencyCode: currencyPair.split('/')[0],
    });

    // Auto-approve FX stabilization withdrawals
    await this.approveWithdrawal(withdrawalId);

    return withdrawalId;
  }
}

export const gruReservePoolService = new GruReservePoolService();

