/**
 * Indy Verifier Agent
 * Verifies Hyperledger Indy credentials and updates on-chain verifier contract
 */

import * as indy from 'indy-sdk';
import { ethers } from 'ethers';

export interface ProofRequest {
    name: string;
    version: string;
    requested_attributes: Record<string, any>;
    requested_predicates: Record<string, any>;
}

export interface IndyConfig {
    poolName: string;
    genesisPath: string;
    walletName: string;
    walletKey: string;
    did: string;
    verifierContractAddress: string;
    verifierAbi: any[];
}

export class IndyVerifierAgent {
    private poolHandle: number | null = null;
    private walletHandle: number | null = null;
    private provider: ethers.JsonRpcProvider;
    private verifierContract: ethers.Contract;
    private signer: ethers.Wallet;

    constructor(
        private config: IndyConfig,
        rpcUrl: string
    ) {
        this.provider = new ethers.JsonRpcProvider(rpcUrl);
        this.verifierContract = new ethers.Contract(
            config.verifierContractAddress,
            config.verifierAbi,
            this.provider
        );
        this.signer = new ethers.Wallet(
            process.env.INDY_VERIFIER_PRIVATE_KEY!,
            this.provider
        );
    }

    /**
     * Initialize Indy pool and wallet
     */
    async initialize(): Promise<void> {
        // Create pool
        await indy.createPoolLedgerConfig(this.config.poolName, {
            genesis_txn: this.config.genesisPath
        });
        
        this.poolHandle = await indy.openPoolLedger(this.config.poolName, null);
        
        // Create wallet
        try {
            await indy.createWallet(
                { id: this.config.walletName },
                { key: this.config.walletKey }
            );
        } catch (error: any) {
            if (!error.message.includes('already exists')) {
                throw error;
            }
        }
        
        this.walletHandle = await indy.openWallet(
            { id: this.config.walletName },
            { key: this.config.walletKey }
        );
        
        console.log('Indy pool and wallet initialized');
    }

    /**
     * Verify credential proof
     */
    async verifyCredentialProof(
        userAddress: string,
        proof: any,
        proofRequest: ProofRequest,
        schemas: any,
        credDefs: any
    ): Promise<{ verified: boolean; score: number }> {
        if (!this.poolHandle || !this.walletHandle) {
            throw new Error('Indy not initialized');
        }

        // Verify the zero-knowledge proof
        const valid = await indy.verifierVerifyProof(
            proofRequest,
            proof,
            schemas,
            credDefs,
            {}, // revoc reg defs
            {}  // revoc regs
        );

        // Calculate score (0-100) based on proof validity and attributes
        const score = valid ? 100 : 0;

        // Update on-chain verifier contract
        if (valid) {
            const proofId = ethers.id(JSON.stringify(proof));
            const verifierWithSigner = this.verifierContract.connect(this.signer);
            
            // Determine credential type from proof request
            const credType = this.getCredentialType(proofRequest);
            
            await verifierWithSigner.verifyCredentialProof(
                userAddress,
                credType,
                ethers.AbiCoder.defaultAbiCoder().encode(['string'], [JSON.stringify(proof)]),
                score,
                proofId
            );
        }

        return { verified: valid, score };
    }

    /**
     * Create proof request for KYC
     */
    createKYCProofRequest(): ProofRequest {
        return {
            name: 'Bridge KYC Verification',
            version: '1.0',
            requested_attributes: {
                'attr1_referent': {
                    name: 'kyc_status',
                    restrictions: [{
                        schema_id: 'did:indy:sovrin:...:schema:kyc:1.0',
                        cred_def_id: 'did:indy:sovrin:...:creddef:...'
                    }]
                },
                'attr2_referent': {
                    name: 'jurisdiction',
                    restrictions: []
                }
            },
            requested_predicates: {
                'predicate1_referent': {
                    name: 'age',
                    p_type: '>=',
                    p_value: 18
                }
            }
        };
    }

    /**
     * Create proof request for accredited investor
     */
    createAccreditedInvestorProofRequest(): ProofRequest {
        return {
            name: 'Accredited Investor Verification',
            version: '1.0',
            requested_attributes: {
                'attr1_referent': {
                    name: 'accredited_status',
                    restrictions: [{
                        schema_id: 'did:indy:sovrin:...:schema:accredited:1.0',
                        cred_def_id: 'did:indy:sovrin:...:creddef:...'
                    }]
                }
            },
            requested_predicates: {}
        };
    }

    /**
     * Get credential type from proof request
     */
    private getCredentialType(proofRequest: ProofRequest): number {
        if (proofRequest.name.includes('KYC')) {
            return 0; // KYC
        } else if (proofRequest.name.includes('Accredited')) {
            return 1; // AccreditedInvestor
        } else if (proofRequest.name.includes('Institution')) {
            return 2; // Institution
        } else if (proofRequest.name.includes('Jurisdiction')) {
            return 3; // Jurisdiction
        }
        return 4; // Compliance
    }

    /**
     * Close wallet and pool
     */
    async close(): Promise<void> {
        if (this.walletHandle) {
            await indy.closeWallet(this.walletHandle);
            this.walletHandle = null;
        }
        
        if (this.poolHandle) {
            await indy.closePoolLedger(this.poolHandle);
            this.poolHandle = null;
        }
    }
}
