// GRU Liquidity Management Service
// GRU Banking Operations Manual - Liquidity Management

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


export interface XAUAnchorMonitoringRequest {
  monitoringDate?: Date;
}

export interface LiquidityDemandEvaluationRequest {
  evaluationDate?: Date;
  indexCode?: string;
}

export interface PredictiveLiquidityRequest {
  timeHorizon: string; // t+1, t+7, t+30, t+90, t+180, t+365
  modelVersion?: string;
}

export interface ReserveBufferAllocationRequest {
  reserveType: string;
  amount: string;
  entityId?: string;
}

export interface LiquidityMonitoringResult {
  monitoringId: string;
  xauAnchorValue: string;
  xauAnchorStability: string;
  stabilityStatus: string;
}

export class GruLiquidityManagementService {
  /**
   * Monitor XAU anchor stability
   */
  async monitorXAUAnchor(
    request?: XAUAnchorMonitoringRequest
  ): Promise<LiquidityMonitoringResult> {
    const monitoringDate = request?.monitoringDate || new Date();
    const monitoringId = `GRU-LIQ-MON-${uuidv4()}`;

    try {
      // Get XAU index value
      const xauIndex = await gruIndexService.getIndex('LiXAU');
      if (!xauIndex) {
        throw new Error('XAU index not found');
      }

      // Calculate stability (based on recent volatility)
      const recentHistory = await prisma.gru_index_price_history.findMany({
        where: {
          indexCode: 'LiXAU',
          timestamp: {
            gte: new Date(monitoringDate.getTime() - 7 * 24 * 60 * 60 * 1000), // Last 7 days
          },
        },
        orderBy: { timestamp: 'desc' },
        take: 7,
      });

      let stability = new Decimal(100); // Default to stable
      if (recentHistory.length > 1) {
        // Calculate volatility
        const values = recentHistory.map((h) => parseFloat(h.indexValue.toString()));
        const avg = values.reduce((a, b) => a + b, 0) / values.length;
        const variance = values.reduce((sum, val) => sum + Math.pow(val - avg, 2), 0) / values.length;
        const stdDev = Math.sqrt(variance);
        const volatility = (stdDev / avg) * 100;

        // Stability is inverse of volatility (0-100 scale)
        stability = new Decimal(Math.max(0, Math.min(100, 100 - volatility * 10)));
      }

      const stabilityStatus = stability.gte(80) ? 'stable' : stability.gte(50) ? 'volatile' : 'critical';

      // Create monitoring record
      const monitoring = await prisma.gru_liquidity_monitoring.create({
        data: {
          monitoringId,
          monitoringDate,
          xauAnchorValue: xauIndex.currentValue,
          xauAnchorStability: stability,
          stabilityStatus,
        },
      });

      return {
        monitoringId,
        xauAnchorValue: xauIndex.currentValue.toString(),
        xauAnchorStability: stability.toString(),
        stabilityStatus,
      };
    } catch (error: any) {
      throw new Error(`XAU anchor monitoring failed: ${error.message}`);
    }
  }

  /**
   * Evaluate index-driven liquidity demand
   */
  async evaluateLiquidityDemand(
    request?: LiquidityDemandEvaluationRequest
  ): Promise<string> {
    const evaluationDate = request?.evaluationDate || new Date();
    const indexCode = request?.indexCode || 'LiXAU';
    const demandId = `GRU-LIQ-DEM-${uuidv4()}`;

    try {
      // Get index value
      const index = await gruIndexService.getIndex(indexCode as any);
      if (!index) {
        throw new Error(`Index not found: ${indexCode}`);
      }

      // Calculate demand level based on index movement
      const recentHistory = await prisma.gru_index_price_history.findMany({
        where: {
          indexCode,
          timestamp: {
            gte: new Date(evaluationDate.getTime() - 24 * 60 * 60 * 1000), // Last 24 hours
          },
        },
        orderBy: { timestamp: 'desc' },
        take: 2,
      });

      let demandLevel = new Decimal(50); // Default normal demand
      let demandType = 'normal';

      if (recentHistory.length >= 2) {
        const current = parseFloat(recentHistory[0].indexValue.toString());
        const previous = parseFloat(recentHistory[1].indexValue.toString());
        const change = ((current - previous) / previous) * 100;

        // High positive change = high demand, high negative change = low demand
        demandLevel = new Decimal(50 + change * 2); // Scale to 0-100
        demandLevel = new Decimal(Math.max(0, Math.min(100, demandLevel.toNumber())));

        if (demandLevel.gte(75)) {
          demandType = 'critical';
        } else if (demandLevel.gte(60)) {
          demandType = 'elevated';
        }
      }

      // Create demand record
      await prisma.gruLiquidityDemand.create({
        data: {
          demandId,
          demandDate: evaluationDate,
          indexCode,
          demandLevel,
          demandType,
        },
      });

      return demandId;
    } catch (error: any) {
      throw new Error(`Liquidity demand evaluation failed: ${error.message}`);
    }
  }

  /**
   * Run predictive liquidity models (t+Δ)
   */
  async runPredictiveModels(
    request: PredictiveLiquidityRequest
  ): Promise<string> {
    const predictionId = `GRU-LIQ-PRED-${uuidv4()}`;
    const modelVersion = request.modelVersion || 'v1.0';

    try {
      // Get current liquidity state
      const pool = await glpService.getOrCreateGlobalPool();
      const poolData = await prisma.global_liquidity_pools.findUnique({
        where: { poolId: pool },
      });

      if (!poolData) {
        throw new Error('Global Liquidity Pool not found');
      }

      // Simple predictive model (in production, this would use ML models)
      const currentLiquidity = poolData.availableLiquidity;
      const timeHorizonDays = this.parseTimeHorizon(request.timeHorizon);

      // Predict liquidity based on historical trends
      const historicalData = await prisma.gru_liquidity_predictions.findMany({
        where: {
          timeHorizon: request.timeHorizon,
          predictionDate: {
            gte: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000), // Last 30 days
          },
        },
        orderBy: { predictionDate: 'desc' },
        take: 10,
      });

      let predictedLiquidity = currentLiquidity;
      let confidenceLevel = new Decimal(50); // Default confidence

      if (historicalData.length > 0) {
        // Calculate average trend
        const trends = historicalData.map((d) => {
          const actual = parseFloat(d.predictedLiquidity.toString());
          return actual;
        });

        const avgTrend = trends.reduce((a, b) => a + b, 0) / trends.length;
        const trendChange = avgTrend - parseFloat(currentLiquidity.toString());
        const dailyChange = trendChange / timeHorizonDays;

        predictedLiquidity = currentLiquidity.plus(new Decimal(dailyChange * timeHorizonDays));
        confidenceLevel = new Decimal(Math.min(100, 50 + historicalData.length * 5));
      }

      // Create prediction record
      await prisma.gru_liquidity_predictions.create({
        data: {
          predictionId,
          predictionDate: new Date(),
          timeHorizon: request.timeHorizon,
          predictedLiquidity,
          confidenceLevel,
          modelVersion,
        },
      });

      return predictionId;
    } catch (error: any) {
      throw new Error(`Predictive liquidity model failed: ${error.message}`);
    }
  }

  /**
   * Allocate supranational reserve buffers
   */
  async allocateReserveBuffers(
    request: ReserveBufferAllocationRequest
  ): Promise<string> {
    const bufferId = `GRU-RES-BUF-${uuidv4()}`;

    try {
      // Get or create reserve buffer
      let buffer = await prisma.gru_reserve_buffers.findFirst({
        where: {
          reserveType: request.reserveType,
          status: 'active',
        },
      });

      if (!buffer) {
        buffer = await prisma.gru_reserve_buffers.create({
          data: {
            bufferId,
            reserveType: request.reserveType,
            bufferAmount: new Decimal(request.amount),
            allocatedAmount: new Decimal(0),
            availableAmount: new Decimal(request.amount),
            status: 'active',
          },
        });
      } else {
        // Update existing buffer
        const amount = new Decimal(request.amount);
        buffer = await prisma.gru_reserve_buffers.update({
          where: { bufferId: buffer.bufferId },
          data: {
            bufferAmount: buffer.bufferAmount.plus(amount),
            availableAmount: buffer.availableAmount.plus(amount),
          },
        });
      }

      // If entityId is provided, allocate to that entity
      if (request.entityId) {
        const allocationAmount = new Decimal(request.amount);
        await prisma.gru_reserve_buffers.update({
          where: { bufferId: buffer.bufferId },
          data: {
            allocatedAmount: buffer.allocatedAmount.plus(allocationAmount),
            availableAmount: buffer.availableAmount.minus(allocationAmount),
            lastAllocatedAt: new Date(),
          },
        });
      }

      return buffer.bufferId;
    } catch (error: any) {
      throw new Error(`Reserve buffer allocation failed: ${error.message}`);
    }
  }

  /**
   * Integrate with Global Liquidity Pool (GLP)
   */
  async integrateWithGLP(): Promise<{
    poolId: string;
    totalLiquidity: string;
    availableLiquidity: string;
  }> {
    try {
      const poolId = await glpService.getOrCreateGlobalPool();
      const pool = await prisma.global_liquidity_pools.findUnique({
        where: { poolId },
      });

      if (!pool) {
        throw new Error('Global Liquidity Pool not found');
      }

      return {
        poolId,
        totalLiquidity: pool.totalLiquidity.toString(),
        availableLiquidity: pool.availableLiquidity.toString(),
      };
    } catch (error: any) {
      throw new Error(`GLP integration failed: ${error.message}`);
    }
  }

  /**
   * Parse time horizon to days
   */
  private parseTimeHorizon(timeHorizon: string): number {
    const match = timeHorizon.match(/t\+(\d+)/);
    if (match) {
      return parseInt(match[1], 10);
    }
    return 1; // Default to 1 day
  }
}

export const gruLiquidityManagementService = new GruLiquidityManagementService();

