// GLP Withdrawal Service
// Withdrawal request processing
// Tier-based access control
// Approval workflows
// Withdrawal execution

import prisma from '@/shared/database/prisma';
import { Decimal } from '@prisma/client/runtime/library';
import { v4 as uuidv4 } from 'uuid';
import { glpService } from './glp-service';


export interface GlpWithdrawalRequestCreate {
  sovereignBankId: string;
  amount: string;
  currencyCode?: string;
  withdrawalTier: string;
  triggerCondition?: string;
  liquidityScore?: Decimal | null;
}

export class GlpWithdrawalService {
  /**
   * Create withdrawal request
   */
  async createWithdrawalRequest(
    request: GlpWithdrawalRequestCreate
  ): Promise<string> {
    const poolId = await glpService.getOrCreateGlobalPool();

    const withdrawalId = `GLP-WD-${uuidv4()}`;

    // Create withdrawal request
    const withdrawal = await prisma.glpWithdrawal.create({
      data: {
        withdrawalId,
        poolId,
        sovereignBankId: request.sovereignBankId,
        amount: new Decimal(request.amount),
        currencyCode: request.currencyCode || null,
        withdrawalTier: request.withdrawalTier,
        triggerCondition: request.triggerCondition || null,
        liquidityScore: request.liquidityScore || null,
        approvalStatus: 'pending',
      },
    });

    // Update pool reserved liquidity
    await this.updateReservedLiquidity(poolId);

    return withdrawalId;
  }

  /**
   * Process automatic withdrawal (Tier 1)
   */
  async processAutomaticWithdrawal(withdrawalId: string): Promise<boolean> {
    const withdrawal = await prisma.glpWithdrawal.findUnique({
      where: { withdrawalId },
    });

    if (!withdrawal) {
      return false;
    }

    if (withdrawal.withdrawalTier !== 'tier_1_automatic') {
      return false;
    }

    // Auto-approve and execute
    await prisma.glpWithdrawal.update({
      where: { withdrawalId },
      data: {
        approvalStatus: 'approved',
        approvedAt: new Date(),
      },
    });

    // Execute withdrawal
    return await this.executeWithdrawal(withdrawalId);
  }

  /**
   * Approve withdrawal (Tier 2 - MSC approval)
   */
  async approveWithdrawal(
    withdrawalId: string,
    approvalEntityId: string
  ): Promise<boolean> {
    const withdrawal = await prisma.glpWithdrawal.findUnique({
      where: { withdrawalId },
    });

    if (!withdrawal) {
      return false;
    }

    if (withdrawal.approvalStatus !== 'pending') {
      return false;
    }

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

    // Execute withdrawal
    return await this.executeWithdrawal(withdrawalId);
  }

  /**
   * Execute withdrawal
   */
  private async executeWithdrawal(withdrawalId: string): Promise<boolean> {
    const withdrawal = await prisma.glpWithdrawal.findUnique({
      where: { withdrawalId },
    });

    if (!withdrawal || withdrawal.approvalStatus !== 'approved') {
      return false;
    }

    // Check available liquidity
    const balance = await glpService.getBalance(withdrawal.currencyCode || undefined);

    if (new Decimal(balance.availableLiquidity).lt(withdrawal.amount)) {
      await prisma.glpWithdrawal.update({
        where: { withdrawalId },
        data: {
          approvalStatus: 'rejected',
        },
      });
      throw new Error('Insufficient available liquidity');
    }

    try {
      // Execute withdrawal
      // In production, this would transfer funds

      // Update withdrawal status
      await prisma.glpWithdrawal.update({
        where: { withdrawalId },
        data: {
          approvalStatus: 'executed',
          executedAt: new Date(),
        },
      });

      // Update pool liquidity
      const poolId = withdrawal.poolId;
      await this.updateReservedLiquidity(poolId);
      await this.updatePoolAfterWithdrawal(poolId, withdrawal.amount);

      return true;
    } catch (error) {
      await prisma.glpWithdrawal.update({
        where: { withdrawalId },
        data: {
          approvalStatus: 'failed',
        },
      });

      throw error;
    }
  }

  /**
   * Reject withdrawal
   */
  async rejectWithdrawal(withdrawalId: string): Promise<boolean> {
    const withdrawal = await prisma.glpWithdrawal.findUnique({
      where: { withdrawalId },
    });

    if (!withdrawal || withdrawal.approvalStatus !== 'pending') {
      return false;
    }

    await prisma.glpWithdrawal.update({
      where: { withdrawalId },
      data: {
        approvalStatus: 'rejected',
      },
    });

    // Update pool reserved liquidity
    await this.updateReservedLiquidity(withdrawal.poolId);

    return true;
  }

  /**
   * Update reserved liquidity
   */
  private async updateReservedLiquidity(poolId: string): Promise<void> {
    const pendingWithdrawals = await prisma.glpWithdrawal.findMany({
      where: {
        poolId,
        approvalStatus: {
          in: ['pending', 'approved'],
        },
      },
    });

    const reserved = pendingWithdrawals.reduce(
      (sum, withdrawal) => sum.plus(withdrawal.amount),
      new Decimal(0)
    );

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

    if (pool) {
      await prisma.globalLiquidityPool.update({
        where: { poolId },
        data: {
          reservedLiquidity: reserved,
          availableLiquidity: pool.totalLiquidity.minus(reserved),
          lastUpdated: new Date(),
        },
      });
    }
  }

  /**
   * Update pool after withdrawal execution
   */
  private async updatePoolAfterWithdrawal(
    poolId: string,
    amount: Decimal
  ): Promise<void> {
    const pool = await prisma.globalLiquidityPool.findUnique({
      where: { poolId },
    });

    if (pool) {
      await prisma.globalLiquidityPool.update({
        where: { poolId },
        data: {
          totalLiquidity: pool.totalLiquidity.minus(amount),
          availableLiquidity: pool.availableLiquidity.minus(amount),
          lastUpdated: new Date(),
        },
      });
    }
  }

  /**
   * Get pending withdrawals
   */
  async getPendingWithdrawals(
    withdrawalTier?: string,
    limit: number = 100
  ) {
    return await prisma.glpWithdrawal.findMany({
      where: {
        approvalStatus: 'pending',
        ...(withdrawalTier && { withdrawalTier }),
      },
      orderBy: { createdAt: 'asc' },
      take: limit,
    });
  }

  /**
   * Get withdrawals for a bank
   */
  async getWithdrawalsForBank(
    sovereignBankId: string,
    approvalStatus?: string,
    limit: number = 100
  ) {
    return await prisma.glpWithdrawal.findMany({
      where: {
        sovereignBankId,
        ...(approvalStatus && { approvalStatus }),
      },
      orderBy: { createdAt: 'desc' },
      take: limit,
    });
  }

  /**
   * Get withdrawal by ID
   */
  async getWithdrawal(withdrawalId: string) {
    return await prisma.glpWithdrawal.findUnique({
      where: { withdrawalId },
      include: {
        pool: true,
      },
    });
  }
}

export const glpWithdrawalService = new GlpWithdrawalService();

