/**
 * @file fabric-besu-bridge.ts
 * @notice Cacti bridge for tokenized assets between Fabric and Besu
 */

import { ethers } from 'ethers';
import { TokenizedEUR } from '../../contracts/tokenization/TokenizedEUR';

export interface FabricBesuBridgeConfig {
    cactiApiUrl: string;
    fabricNetworkId: string;
    besuNetworkId: string;
    besuProvider: ethers.Provider;
    tokenizedEURAddress: string;
    tokenizedEURAbi: any[];
}

export interface BridgeTransferRequest {
    fabricTokenId: string;
    fabricTxHash: string;
    amount: string;
    recipient: string;
    attestation: {
        fabricTxHash: string;
        tokenId: string;
        amount: string;
        minter: string;
        timestamp: number;
        signature: string;
    };
}

export class FabricBesuBridge {
    private config: FabricBesuBridgeConfig;
    private tokenizedEUR: ethers.Contract;

    constructor(config: FabricBesuBridgeConfig) {
        this.config = config;
        this.tokenizedEUR = new ethers.Contract(
            config.tokenizedEURAddress,
            config.tokenizedEURAbi,
            config.besuProvider
        );
    }

    /**
     * Bridge tokenized asset from Fabric to Besu
     */
    async bridgeToBesu(request: BridgeTransferRequest): Promise<{ txHash: string; blockNumber: number }> {
        // Step 1: Verify Fabric transaction via Cacti
        const fabricTx = await this.verifyFabricTransaction(request.fabricTxHash);
        if (!fabricTx.valid) {
            throw new Error('Invalid Fabric transaction');
        }

        // Step 2: Create bridge transfer via Cacti
        const bridgeResponse = await fetch(`${this.config.cactiApiUrl}/api/v1/plugins/ledger-connector/bridge/transfer`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                sourceNetwork: this.config.fabricNetworkId,
                targetNetwork: this.config.besuNetworkId,
                sourceTxHash: request.fabricTxHash,
                targetContract: this.config.tokenizedEURAddress,
                functionName: 'mintFromFabric',
                args: [
                    request.recipient,
                    request.amount,
                    request.fabricTokenId,
                    request.fabricTxHash,
                    request.attestation
                ]
            })
        });

        if (!bridgeResponse.ok) {
            throw new Error('Cacti bridge transfer failed');
        }

        const bridgeResult = await bridgeResponse.json();

        // Step 3: Wait for Besu transaction confirmation
        const receipt = await this.config.besuProvider.getTransactionReceipt(bridgeResult.txHash);
        if (!receipt) {
            throw new Error('Transaction receipt not found');
        }

        return {
            txHash: bridgeResult.txHash,
            blockNumber: Number(receipt.blockNumber)
        };
    }

    /**
     * Bridge tokenized asset from Besu to Fabric (redemption)
     */
    async bridgeToFabric(
        besuTxHash: string,
        fabricTokenId: string,
        amount: string,
        redeemer: string
    ): Promise<{ fabricTxHash: string }> {
        // Step 1: Verify Besu transaction
        const besuTx = await this.config.besuProvider.getTransaction(besuTxHash);
        if (!besuTx) {
            throw new Error('Besu transaction not found');
        }

        // Step 2: Create redemption on Fabric via Cacti
        const redemptionRequest = {
            tokenId: fabricTokenId,
            redeemer: redeemer,
            amount: amount,
            redemptionProof: besuTxHash
        };

        const response = await fetch(`${this.config.cactiApiUrl}/api/v1/plugins/ledger-connector/fabric/invoke`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                chaincodeId: 'tokenized-asset',
                functionName: 'RedeemToken',
                args: [JSON.stringify(redemptionRequest)]
            })
        });

        if (!response.ok) {
            throw new Error('Fabric redemption failed');
        }

        const result = await response.json();
        return {
            fabricTxHash: result.txId
        };
    }

    /**
     * Verify Fabric transaction via Cacti
     */
    private async verifyFabricTransaction(fabricTxHash: string): Promise<{ valid: boolean; data?: any }> {
        try {
            const response = await fetch(
                `${this.config.cactiApiUrl}/api/v1/plugins/ledger-connector/fabric/query`,
                {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({
                        chaincodeId: 'tokenized-asset',
                        functionName: 'GetToken',
                        args: [fabricTxHash]
                    })
                }
            );

            if (!response.ok) {
                return { valid: false };
            }

            const result = await response.json();
            return { valid: true, data: result };
        } catch (error) {
            console.error('Fabric transaction verification error:', error);
            return { valid: false };
        }
    }

    /**
     * Synchronize state between Fabric and Besu
     */
    async synchronizeState(fabricTokenId: string): Promise<{ synced: boolean; differences?: any }> {
        // Get Fabric state
        const fabricState = await this.getFabricState(fabricTokenId);

        // Get Besu state
        const besuBalance = await this.tokenizedEUR.getFabricTokenBalance(fabricTokenId);

        // Compare states
        const fabricAmount = parseFloat(fabricState?.amount || '0');
        const besuAmount = parseFloat(ethers.formatEther(besuBalance));

        if (Math.abs(fabricAmount - besuAmount) > 0.01) {
            return {
                synced: false,
                differences: {
                    fabric: fabricAmount,
                    besu: besuAmount,
                    difference: fabricAmount - besuAmount
                }
            };
        }

        return { synced: true };
    }

    /**
     * Get Fabric state
     */
    private async getFabricState(fabricTokenId: string): Promise<any> {
        try {
            const response = await fetch(
                `${this.config.cactiApiUrl}/api/v1/plugins/ledger-connector/fabric/query`,
                {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({
                        chaincodeId: 'tokenized-asset',
                        functionName: 'GetToken',
                        args: [fabricTokenId]
                    })
                }
            );

            if (!response.ok) {
                return null;
            }

            return await response.json();
        } catch (error) {
            console.error('Get Fabric state error:', error);
            return null;
        }
    }
}
