// Global CBDC Tokenomics Framework (GCTF) - Core Service

import { Decimal } from 'decimal.js';
import {
  CBDCUnitType,
  CBDCIssuancePolicy,
  CBDCSupplyMechanics,
  CBDCIssuanceRule,
  CBDCVelocityControl,
  CBDCTokenomicsConfig,
  CBDCIssuanceRequest,
  CBDCBurnRequest,
  CBDCSupplyAdjustment,
} from './types';
import { logger } from '@/infrastructure/monitoring/logger';
import { v4 as uuidv4 } from 'uuid';

export class GCTFService {
  /**
   * Calculate CBDC issuance using the rule:
   * CBDC_new = demand_adjustment + (settlement_volume * factor) - stress_penalties
   */
  calculateIssuanceRule(
    demandAdjustment: number,
    settlementVolume: string,
    factor: number,
    stressPenalties: number
  ): CBDCIssuanceRule {
    const settlementDecimal = new Decimal(settlementVolume);
    const calculatedSupply = new Decimal(demandAdjustment)
      .plus(settlementDecimal.times(factor))
      .minus(stressPenalties)
      .toString();

    return {
      demandAdjustment,
      settlementVolume,
      factor,
      stressPenalties,
      calculatedSupply,
    };
  }

  /**
   * Get or create supply mechanics for a CBDC unit
   */
  async getSupplyMechanics(
    sovereignBankId: string,
    unitType: CBDCUnitType
  ): Promise<CBDCSupplyMechanics> {
    // In real implementation, fetch from database
    // For now, return default structure
    return {
      sovereignBankId,
      unitType,
      currentSupply: '0',
      issuancePolicy: CBDCIssuancePolicy.DEMAND_DRIVEN,
      lastIssuance: new Date(),
      lastBurn: new Date(),
    };
  }

  /**
   * Issue new CBDC tokens
   */
  async issueCBDC(request: CBDCIssuanceRequest): Promise<CBDCSupplyAdjustment> {
    logger.info(`GCTF: Issuing ${request.amount} ${request.unitType} for ${request.sovereignBankId}`);

    // Verify issuance is allowed based on policy
    const supplyMechanics = await this.getSupplyMechanics(
      request.sovereignBankId,
      request.unitType
    );

    // Check max supply if set
    if (supplyMechanics.maxSupply) {
      const current = new Decimal(supplyMechanics.currentSupply);
      const max = new Decimal(supplyMechanics.maxSupply);
      const requested = new Decimal(request.amount);

      if (current.plus(requested).gt(max)) {
        throw new Error(
          `Issuance would exceed max supply. Current: ${current}, Max: ${max}, Requested: ${requested}`
        );
      }
    }

    // Verify reserve backing if required
    if (supplyMechanics.issuancePolicy === CBDCIssuancePolicy.RESERVE_BACKED) {
      await this.verifyReserveBacking(
        request.sovereignBankId,
        request.unitType,
        request.amount
      );
    }

    const adjustment: CBDCSupplyAdjustment = {
      sovereignBankId: request.sovereignBankId,
      unitType: request.unitType,
      adjustment: request.amount,
      reason: request.reason,
      timestamp: new Date(),
    };

    // In real implementation, update database and ledger
    logger.info(`GCTF: Issued ${request.amount} ${request.unitType}`);

    return adjustment;
  }

  /**
   * Burn CBDC tokens
   */
  async burnCBDC(request: CBDCBurnRequest): Promise<CBDCSupplyAdjustment> {
    logger.info(`GCTF: Burning ${request.amount} ${request.unitType} for ${request.sovereignBankId}`);

    const supplyMechanics = await this.getSupplyMechanics(
      request.sovereignBankId,
      request.unitType
    );

    const current = new Decimal(supplyMechanics.currentSupply);
    const requested = new Decimal(request.amount);

    if (requested.gt(current)) {
      throw new Error(
        `Burn amount exceeds current supply. Current: ${current}, Requested: ${requested}`
      );
    }

    const adjustment: CBDCSupplyAdjustment = {
      sovereignBankId: request.sovereignBankId,
      unitType: request.unitType,
      adjustment: `-${request.amount}`, // Negative for burn
      reason: request.reason,
      timestamp: new Date(),
    };

    // In real implementation, update database and ledger
    logger.info(`GCTF: Burned ${request.amount} ${request.unitType}`);

    return adjustment;
  }

  /**
   * Configure velocity controls for a CBDC unit
   */
  async configureVelocityControls(
    sovereignBankId: string,
    unitType: CBDCUnitType,
    controls: Partial<CBDCVelocityControl>
  ): Promise<CBDCVelocityControl> {
    logger.info(`GCTF: Configuring velocity controls for ${unitType}`, {
      sovereignBankId,
    });

    const velocityControl: CBDCVelocityControl = {
      sovereignBankId,
      unitType,
      ...controls,
    };

    // In real implementation, save to database
    return velocityControl;
  }

  /**
   * Enable sovereign quarantine mode
   */
  async enableQuarantineMode(
    sovereignBankId: string,
    unitType: CBDCUnitType,
    endDate?: Date
  ): Promise<CBDCVelocityControl> {
    logger.warn(`GCTF: Enabling quarantine mode for ${unitType}`, {
      sovereignBankId,
    });

    const controls = await this.configureVelocityControls(sovereignBankId, unitType, {
      sovereignQuarantineMode: true,
      quarantineStartDate: new Date(),
      quarantineEndDate: endDate,
    });

    return controls;
  }

  /**
   * Disable sovereign quarantine mode
   */
  async disableQuarantineMode(
    sovereignBankId: string,
    unitType: CBDCUnitType
  ): Promise<CBDCVelocityControl> {
    logger.info(`GCTF: Disabling quarantine mode for ${unitType}`, {
      sovereignBankId,
    });

    const controls = await this.configureVelocityControls(sovereignBankId, unitType, {
      sovereignQuarantineMode: false,
      quarantineEndDate: new Date(),
    });

    return controls;
  }

  /**
   * Get tokenomics configuration
   */
  async getTokenomicsConfig(
    sovereignBankId: string,
    unitType: CBDCUnitType
  ): Promise<CBDCTokenomicsConfig> {
    const supplyMechanics = await this.getSupplyMechanics(sovereignBankId, unitType);

    // In real implementation, fetch velocity controls from database
    const velocityControls: CBDCVelocityControl | undefined = undefined;

    const config: CBDCTokenomicsConfig = {
      sovereignBankId,
      unitType,
      supplyMechanics,
      velocityControls,
      monetaryPolicy: {
        reserveRequirement: 0.1, // 10% default
      },
      programmableCompliance: {
        enabled: true,
        rules: [],
      },
    };

    return config;
  }

  /**
   * Verify reserve backing for issuance
   */
  private async verifyReserveBacking(
    sovereignBankId: string,
    unitType: CBDCUnitType,
    amount: string
  ): Promise<void> {
    // In real implementation, check reserve accounts
    logger.info(`GCTF: Verifying reserve backing for ${amount} ${unitType}`);
    // Placeholder - would check actual reserves
  }

  /**
   * Apply velocity controls to a transaction
   */
  async checkVelocityControls(
    sovereignBankId: string,
    unitType: CBDCUnitType,
    transactionAmount: string,
    category?: string
  ): Promise<{ allowed: boolean; reason?: string }> {
    // In real implementation, fetch velocity controls and check
    const controls = await this.getVelocityControls(sovereignBankId, unitType);

    if (controls?.sovereignQuarantineMode) {
      return {
        allowed: false,
        reason: 'Sovereign quarantine mode is active',
      };
    }

    // Check spending category restrictions
    if (category && controls?.spendingCategoryRestrictions) {
      const restriction = controls.spendingCategoryRestrictions.find(
        (r) => r.category === category
      );
      if (restriction && !restriction.allowed) {
        return {
          allowed: false,
          reason: `Category ${category} is restricted`,
        };
      }
      if (restriction && new Decimal(transactionAmount).gt(restriction.maxAmount)) {
        return {
          allowed: false,
          reason: `Transaction amount exceeds category limit`,
        };
      }
    }

    // Check cross-border routing limits
    // This would be checked in the routing layer

    return { allowed: true };
  }

  /**
   * Get velocity controls
   */
  private async getVelocityControls(
    sovereignBankId: string,
    unitType: CBDCUnitType
  ): Promise<CBDCVelocityControl | null> {
    // In real implementation, fetch from database
    return null;
  }
}

export const gctfService = new GCTFService();

