// DBIS Temporal Liquidity Portal - Liquidity Service
// Future liquidity prediction and borrowing

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


export interface CreateFutureReserveRequest {
  portalId: string;
  predictedTime: Date; // t+Δ
  predictedReserves: number;
  confidenceLevel: number;
}

export interface BorrowLiquidityRequest {
  portalId: string;
  amount: number;
  targetTime?: Date;
}

export class TlpLiquidityService {
  /**
   * Create future liquidity reserve
   * Formula: available_liquidity = predicted_reserves * confidence_level
   */
  async createFutureReserve(request: CreateFutureReserveRequest) {
    const portal = await tlpPortalService.getPortal(request.portalId);
    if (!portal) {
      throw new Error(`Portal not found: ${request.portalId}`);
    }

    if (request.confidenceLevel < 0 || request.confidenceLevel > 1) {
      throw new Error('Confidence level must be between 0 and 1');
    }

    const reserveId = `FLR-${uuidv4()}`;

    const predictedReserves = new Decimal(request.predictedReserves);
    const confidenceLevel = new Decimal(request.confidenceLevel);
    const availableLiquidity = predictedReserves.times(confidenceLevel);

    // Calculate expiry (predicted time + buffer)
    const expiryAt = new Date(request.predictedTime);
    expiryAt.setSeconds(expiryAt.getSeconds() + portal.targetTimeDelta);

    const reserve = await prisma.future_liquidity_reserves.create({
      data: {
        reserveId,
        portalId: request.portalId,
        predictedTime: request.predictedTime,
        predictedReserves: predictedReserves,
        confidenceLevel: confidenceLevel,
        availableLiquidity: availableLiquidity,
        borrowedAmount: new Decimal(0),
        status: 'available',
        expiresAt: expiryAt,
      },
    });

    return reserve;
  }

  /**
   * Borrow liquidity from future reserves
   */
  async borrowLiquidity(request: BorrowLiquidityRequest) {
    const portal = await tlpPortalService.getPortal(request.portalId);
    if (!portal) {
      throw new Error(`Portal not found: ${request.portalId}`);
    }

    const borrowAmount = new Decimal(request.amount);

    // Check max borrow limit
    if (borrowAmount.gt(portal.maxLiquidityBorrow)) {
      throw new Error(`Borrow amount exceeds maximum: ${portal.maxLiquidityBorrow}`);
    }

    // Find available reserves
    const reserves = await prisma.future_liquidity_reserves.findMany({
      where: {
        portalId: request.portalId,
        status: 'available',
        expiresAt: {
          gt: request.targetTime || new Date(),
        },
      },
      orderBy: { predictedTime: 'asc' },
    });

    let remainingAmount = borrowAmount;
    const usedReserves: string[] = [];

    for (const reserve of reserves) {
      if (remainingAmount.lte(0)) {
        break;
      }

      const available = reserve.availableLiquidity.minus(reserve.borrowedAmount);
      if (available.lte(0)) {
        continue;
      }

      const borrowFromReserve = remainingAmount.gt(available) ? available : remainingAmount;
      const newBorrowedAmount = reserve.borrowedAmount.plus(borrowFromReserve);

      await prisma.future_liquidity_reserves.update({
        where: { reserveId: reserve.reserveId },
        data: {
          borrowedAmount: newBorrowedAmount,
          status: newBorrowedAmount.gte(reserve.availableLiquidity) ? 'exhausted' : 'available',
        },
      });

      usedReserves.push(reserve.reserveId);
      remainingAmount = remainingAmount.minus(borrowFromReserve);
    }

    if (remainingAmount.gt(0)) {
      throw new Error(`Insufficient future liquidity. Remaining: ${remainingAmount}`);
    }

    return {
      borrowedAmount: request.amount,
      usedReserves,
      portalId: request.portalId,
    };
  }

  /**
   * Get available liquidity for a portal
   */
  async getAvailableLiquidity(portalId: string) {
    const reserves = await prisma.future_liquidity_reserves.findMany({
      where: {
        portalId,
        status: 'available',
        expiresAt: {
          gt: new Date(),
        },
      },
    });

    let totalAvailable = new Decimal(0);

    for (const reserve of reserves) {
      const available = reserve.availableLiquidity.minus(reserve.borrowedAmount);
      if (available.gt(0)) {
        totalAvailable = totalAvailable.plus(available);
      }
    }

    return {
      portalId,
      totalAvailable: totalAvailable.toNumber(),
      reserveCount: reserves.length,
    };
  }

  /**
   * Get future reserves for a portal
   */
  async getFutureReserves(portalId: string) {
    return await prisma.future_liquidity_reserves.findMany({
      where: { portalId },
      orderBy: { predictedTime: 'asc' },
    });
  }
}

export const tlpLiquidityService = new TlpLiquidityService();

