// GRU Service
// Global Reserve Unit hierarchy management (1 M00 = 5 M0 = 25 M1)

import prisma from '@/shared/database/prisma';
import { Decimal } from '@prisma/client/runtime/library';
import { v4 as uuidv4 } from 'uuid';


export interface GruUnitRequest {
  unitType: 'M00' | 'M0' | 'M1';
  amount: string;
  targetType?: 'M00' | 'M0' | 'M1';
}

export interface GruConversionResult {
  sourceAmount: Decimal;
  sourceType: string;
  targetAmount: Decimal;
  targetType: string;
  conversionRate: Decimal;
}

export class GruService {
  // GRU Hierarchy Constants
  private readonly M00_TO_M0_RATIO = new Decimal(5); // 1 M00 = 5 M0
  private readonly M00_TO_M1_RATIO = new Decimal(25); // 1 M00 = 25 M1
  private readonly M0_TO_M1_RATIO = new Decimal(5); // 1 M0 = 5 M1

  /**
   * Convert between GRU unit types
   * 1 M00 = 5 M0 = 25 M1
   */
  async convertGruUnits(
    request: GruUnitRequest
  ): Promise<GruConversionResult> {
    const sourceAmount = new Decimal(request.amount);
    const targetType = request.targetType || this.getDefaultTargetType(request.unitType);

    let targetAmount: Decimal;
    let conversionRate: Decimal;

    if (request.unitType === targetType) {
      // No conversion needed
      targetAmount = sourceAmount;
      conversionRate = new Decimal(1);
    } else {
      // Convert to M00 first (base unit)
      const m00Amount = this.convertToM00(sourceAmount, request.unitType);

      // Convert from M00 to target
      targetAmount = this.convertFromM00(m00Amount, targetType);
      conversionRate = targetAmount.dividedBy(sourceAmount);
    }

    // Save conversion record
    await prisma.gru_conversions.create({
      data: {
        conversionId: `GRU-CONV-${uuidv4()}`,
        sourceAmount,
        sourceType: request.unitType,
        targetAmount,
        targetType,
        conversionRate,
        status: 'completed',
      },
    });

    return {
      sourceAmount,
      sourceType: request.unitType,
      targetAmount,
      targetType,
      conversionRate,
    };
  }

  /**
   * Convert to M00 (base unit)
   */
  private convertToM00(amount: Decimal, unitType: 'M00' | 'M0' | 'M1'): Decimal {
    switch (unitType) {
      case 'M00':
        return amount;
      case 'M0':
        return amount.dividedBy(this.M00_TO_M0_RATIO);
      case 'M1':
        return amount.dividedBy(this.M00_TO_M1_RATIO);
      default:
        throw new Error(`Invalid unit type: ${unitType}`);
    }
  }

  /**
   * Convert from M00 to target type
   */
  private convertFromM00(amount: Decimal, targetType: 'M00' | 'M0' | 'M1'): Decimal {
    switch (targetType) {
      case 'M00':
        return amount;
      case 'M0':
        return amount.times(this.M00_TO_M0_RATIO);
      case 'M1':
        return amount.times(this.M00_TO_M1_RATIO);
      default:
        throw new Error(`Invalid target type: ${targetType}`);
    }
  }

  /**
   * Get default target type for conversion
   */
  private getDefaultTargetType(sourceType: 'M00' | 'M0' | 'M1'): 'M00' | 'M0' | 'M1' {
    // Default: convert to next lower tier
    switch (sourceType) {
      case 'M00':
        return 'M0';
      case 'M0':
        return 'M1';
      case 'M1':
        return 'M00'; // Wrap around
      default:
        return 'M00';
    }
  }

  /**
   * Create mixed GRU composition
   * 1 M00 = x M0 + y M1 (where x + y/5 = 5)
   */
  async createMixedGruComposition(
    m00Amount: Decimal,
    m0Amount: Decimal,
    m1Amount: Decimal
  ): Promise<string> {
    // Verify composition: 1 M00 = 5 M0 = 25 M1
    const m00FromM0 = m0Amount.dividedBy(this.M00_TO_M0_RATIO);
    const m00FromM1 = m1Amount.dividedBy(this.M00_TO_M1_RATIO);
    const totalM00 = m00Amount.plus(m00FromM0).plus(m00FromM1);

    const compositionId = `GRU-COMP-${uuidv4()}`;

    await prisma.gruComposition.create({
      data: {
        compositionId,
        m00Amount,
        m0Amount,
        m1Amount,
        totalM00Equivalent: totalM00,
        status: 'active',
      },
    });

    return compositionId;
  }

  /**
   * Get GRU unit by ID
   */
  async getGruUnit(gruUnitId: string) {
    return await prisma.gru_units.findUnique({
      where: { gruUnitId },
    });
  }

  /**
   * Get GRU units for a bank
   */
  async getGruUnitsForBank(sovereignBankId: string) {
    return await prisma.gru_units.findMany({
      where: { sovereignBankId },
      orderBy: { createdAt: 'desc' },
    });
  }

  /**
   * Get conversion history
   */
  async getConversionHistory(limit: number = 100) {
    return await prisma.gru_conversions.findMany({
      orderBy: { createdAt: 'desc' },
      take: limit,
    });
  }
}

export const gruService = new GruService();

