/**
 * @file gru-tokenization.ts
 * @notice GRU integration for tokenized assets (Sub-Volume B)
 */

import { gruValuationService } from './gru/gru-valuation.service';
import { gruSettlementPipelineService } from './gru/gru-settlement-pipeline.service';
import { TokenRegistry } from '../../../smom-dbis-138/contracts/tokenization/TokenRegistry';

export interface TokenizedAssetGruValuationRequest {
    tokenAddress: string;
    underlyingAsset: string;
    amount: string;
    targetCurrency?: string; // Default: GRU
}

export interface TokenizedAssetGruSettlementRequest {
    tokenAddress: string;
    fabricTokenId: string;
    sourceBankId: string;
    destinationBankId: string;
    amount: string;
    currencyCode: string;
}

export class GruTokenizationIntegration {
    private tokenRegistry: TokenRegistry;

    constructor(tokenRegistry: TokenRegistry) {
        this.tokenRegistry = tokenRegistry;
    }

    /**
     * Value tokenized asset in GRU
     */
    async valueTokenizedAssetInGRU(
        request: TokenizedAssetGruValuationRequest
    ): Promise<{
        tokenAddress: string;
        underlyingAsset: string;
        amount: string;
        gruValue: string;
        gruUnit: 'M00' | 'M0' | 'M1';
        xauRate: string;
        triangulationRate: string;
    }> {
        // Get token metadata
        const tokenMetadata = await this.tokenRegistry.getToken(request.tokenAddress);

        // Value underlying asset in GRU via XAU triangulation
        const valuationResult = await gruValuationService.valueAssetInGRU({
            assetType: request.underlyingAsset,
            amount: parseFloat(request.amount),
            targetCurrency: request.targetCurrency || 'GRU'
        });

        return {
            tokenAddress: request.tokenAddress,
            underlyingAsset: request.underlyingAsset,
            amount: request.amount,
            gruValue: valuationResult.gruValue.toString(),
            gruUnit: valuationResult.gruUnit as 'M00' | 'M0' | 'M1',
            xauRate: valuationResult.xauRate.toString(),
            triangulationRate: valuationResult.triangulationRate.toString()
        };
    }

    /**
     * Execute tokenized asset settlement via GRU pipeline
     */
    async executeTokenizedAssetGruSettlement(
        request: TokenizedAssetGruSettlementRequest
    ): Promise<{
        pipelineId: string;
        settlementId: string;
        gruSettlementId: string;
        status: string;
    }> {
        // Get token metadata
        const tokenMetadata = await this.tokenRegistry.getToken(request.tokenAddress);

        // Execute GRU settlement pipeline
        const pipelineResult = await gruSettlementPipelineService.processSettlementPipeline({
            sourceBankId: request.sourceBankId,
            destinationBankId: request.destinationBankId,
            amount: request.amount,
            currencyCode: request.currencyCode,
            assetType: 'tokenized',
            assetDetails: {
                tokenAddress: request.tokenAddress,
                fabricTokenId: request.fabricTokenId,
                underlyingAsset: tokenMetadata.underlyingAsset
            }
        });

        return {
            pipelineId: pipelineResult.pipelineId,
            settlementId: pipelineResult.settlementId,
            gruSettlementId: pipelineResult.gruSettlementId || '',
            status: pipelineResult.status
        };
    }

    /**
     * Get FX rate for tokenized asset
     */
    async getTokenizedAssetFXRate(
        tokenAddress: string,
        targetCurrency: string
    ): Promise<{
        fromCurrency: string;
        toCurrency: string;
        rate: string;
        xauRate: string;
    }> {
        const tokenMetadata = await this.tokenRegistry.getToken(tokenAddress);

        // Get FX rate via GRU/XAU triangulation
        const fxRate = await gruValuationService.getFXRate(
            tokenMetadata.underlyingAsset,
            targetCurrency
        );

        return {
            fromCurrency: tokenMetadata.underlyingAsset,
            toCurrency: targetCurrency,
            rate: fxRate.rate.toString(),
            xauRate: fxRate.xauRate.toString()
        };
    }

    /**
     * Convert tokenized asset to GRU
     */
    async convertTokenizedAssetToGRU(
        tokenAddress: string,
        amount: string,
        targetGRUUnit: 'M00' | 'M0' | 'M1' = 'M00'
    ): Promise<{
        tokenAmount: string;
        gruAmount: string;
        gruUnit: string;
        conversionRate: string;
    }> {
        const valuation = await this.valueTokenizedAssetInGRU({
            tokenAddress,
            underlyingAsset: 'EUR', // Will be determined from token metadata
            amount
        });

        // Convert to target GRU unit if needed
        let gruAmount = valuation.gruValue;
        if (targetGRUUnit !== valuation.gruUnit) {
            // Apply GRU unit conversion
            gruAmount = this.convertGRUUnit(valuation.gruValue, valuation.gruUnit, targetGRUUnit);
        }

        return {
            tokenAmount: amount,
            gruAmount,
            gruUnit: targetGRUUnit,
            conversionRate: valuation.triangulationRate
        };
    }

    /**
     * Convert GRU unit
     */
    private convertGRUUnit(
        amount: string,
        fromUnit: 'M00' | 'M0' | 'M1',
        toUnit: 'M00' | 'M0' | 'M1'
    ): string {
        // GRU conversion rates:
        // 1 M00 = 5 M0
        // 1 M00 = 25 M1
        // 1 M0 = 5 M1

        const amountNum = parseFloat(amount);

        if (fromUnit === toUnit) {
            return amount;
        }

        // Convert to M00 first
        let m00Amount: number;
        if (fromUnit === 'M00') {
            m00Amount = amountNum;
        } else if (fromUnit === 'M0') {
            m00Amount = amountNum / 5;
        } else { // M1
            m00Amount = amountNum / 25;
        }

        // Convert from M00 to target unit
        if (toUnit === 'M00') {
            return m00Amount.toString();
        } else if (toUnit === 'M0') {
            return (m00Amount * 5).toString();
        } else { // M1
            return (m00Amount * 25).toString();
        }
    }
}
