import { ethers } from 'ethers';
import * as dotenv from 'dotenv';
import path from 'path';

dotenv.config();
// Fill contract addresses from config/smart-contracts-master.json when not set in .env (CJS-safe)
try {
  const loaderPath = path.resolve(__dirname, '../../../../config/contracts-loader.cjs');
  // eslint-disable-next-line @typescript-eslint/no-var-requires
  const { loadContractsIntoProcessEnv } = require(loaderPath);
  if (typeof loadContractsIntoProcessEnv === 'function') loadContractsIntoProcessEnv([138, 1]);
} catch (_) { /* optional: master JSON not found when run outside repo */ }

/**
 * State Anchoring Service
 * 
 * Monitors ChainID 138 blocks and submits state proofs to MainnetTether contract
 */

// Contract addresses (from .env or config/smart-contracts-master.json)
const TETHER_ADDRESS =
    process.env.TETHER_ADDRESS ||
    process.env.MAINNET_TETHER_ADDRESS ||
    '0x15DF1D5BFDD8Aa4b380445D4e3E9B38d34283619';
const CHAIN138_RPC = process.env.CHAIN138_RPC_URL || 'http://192.168.11.211:8545';
const MAINNET_RPC = process.env.MAINNET_RPC_URL || 'https://eth.llamarpc.com';

// Contract ABI (simplified - includes only needed functions)
const TETHER_ABI = [
    "function anchorStateProof(uint256 blockNumber, bytes32 blockHash, bytes32 stateRoot, bytes32 previousBlockHash, uint256 timestamp, bytes calldata signatures, uint256 validatorCount) external",
    "function stateProofs(uint256) external view returns (uint256 blockNumber, bytes32 blockHash, bytes32 stateRoot, bytes32 previousBlockHash, uint256 timestamp, bytes memory signatures, uint256 validatorCount, bytes32 proofHash)",
    "function isAnchored(uint256) external view returns (bool)",
    "event StateProofAnchored(uint256 indexed blockNumber, bytes32 indexed blockHash, bytes32 indexed stateRoot, uint256 timestamp, uint256 validatorCount)"
];

interface StateProof {
    blockNumber: number;
    blockHash: string;
    stateRoot: string;
    previousBlockHash: string;
    timestamp: number;
    signatures: string;
    validatorCount: number;
}

class StateAnchoringService {
    private chain138Provider: ethers.JsonRpcProvider;
    private mainnetProvider: ethers.JsonRpcProvider;
    private mainnetWallet: ethers.Wallet;
    private tetherContract: ethers.Contract;
    private isRunning: boolean = false;

    constructor() {
        if (!process.env.PRIVATE_KEY) {
            throw new Error('PRIVATE_KEY environment variable is required');
        }

        this.chain138Provider = new ethers.JsonRpcProvider(CHAIN138_RPC);
        this.mainnetProvider = new ethers.JsonRpcProvider(MAINNET_RPC);
        this.mainnetWallet = new ethers.Wallet(process.env.PRIVATE_KEY, this.mainnetProvider);
        this.tetherContract = new ethers.Contract(TETHER_ADDRESS, TETHER_ABI, this.mainnetWallet);
    }

    /**
     * Start monitoring blocks and anchoring state proofs
     */
    async start() {
        if (this.isRunning) {
            console.log('Service already running');
            return;
        }

        this.isRunning = true;
        console.log('Starting State Anchoring Service...');
        console.log(`ChainID 138 RPC: ${CHAIN138_RPC}`);
        console.log(`Mainnet RPC: ${MAINNET_RPC}`);
        console.log(`MainnetTether: ${TETHER_ADDRESS}`);
        console.log(`Admin: ${this.mainnetWallet.address}`);

        // Monitor new blocks
        this.chain138Provider.on('block', async (blockNumber) => {
            try {
                await this.processBlock(blockNumber);
            } catch (error) {
                console.error(`Error processing block ${blockNumber}:`, error);
            }
        });

        console.log('Service started. Monitoring blocks...');
    }

    /**
     * Process a block and anchor state proof if needed
     */
    async processBlock(blockNumber: number) {
        console.log(`Processing block ${blockNumber}...`);

        // Get block data
        const block = await this.chain138Provider.getBlock(blockNumber);
        if (!block) {
            console.warn(`Block ${blockNumber} not found`);
            return;
        }

        // Check if already anchored
        try {
            const isAnchored = await this.tetherContract.isAnchored(blockNumber);
            if (isAnchored) {
                console.log(`Block ${blockNumber} already anchored, skipping`);
                return;
            }
        } catch (error) {
            console.warn(`Could not check if block ${blockNumber} is anchored:`, error);
        }

        // Collect validator signatures (placeholder - implement based on your validator setup)
        const signatures = await this.collectSignatures(blockNumber);

        if (signatures.validatorCount === 0) {
            console.warn(`No signatures collected for block ${blockNumber}, skipping`);
            return;
        }

        // Submit state proof
        await this.submitStateProof({
            blockNumber,
            blockHash: block.hash || '',
            stateRoot: block.stateRoot || '',
            previousBlockHash: block.parentHash,
            timestamp: Number(block.timestamp),
            signatures: signatures.signatures,
            validatorCount: signatures.validatorCount
        });
    }

    /**
     * Collect validator signatures for a block
     * TODO: Implement based on your ChainID 138 validator setup
     */
    async collectSignatures(blockNumber: number): Promise<{ signatures: string; validatorCount: number }> {
        // Placeholder implementation
        // In production, this would:
        // 1. Query ChainID 138 validators
        // 2. Request signatures for the block
        // 3. Aggregate signatures
        // 4. Return aggregated signature bytes and count

        console.log(`Collecting signatures for block ${blockNumber}...`);

        // For now, return empty (service won't anchor without signatures)
        // This allows the service to run and monitor, but won't submit until signatures are implemented
        return {
            signatures: '0x',
            validatorCount: 0
        };
    }

    /**
     * Submit state proof to MainnetTether
     */
    async submitStateProof(proof: StateProof) {
        try {
            console.log(`Submitting state proof for block ${proof.blockNumber}...`);

            const tx = await this.tetherContract.anchorStateProof(
                proof.blockNumber,
                proof.blockHash,
                proof.stateRoot,
                proof.previousBlockHash,
                proof.timestamp,
                proof.signatures,
                proof.validatorCount
            );

            console.log(`Transaction submitted: ${tx.hash}`);
            const receipt = await tx.wait();
            console.log(`✓ State proof anchored for block ${proof.blockNumber} (tx: ${tx.hash})`);
        } catch (error: any) {
            if (error.message?.includes('already processed') || error.message?.includes('already anchored')) {
                console.log(`Block ${proof.blockNumber} already anchored`);
            } else {
                console.error(`Failed to anchor state proof for block ${proof.blockNumber}:`, error);
                throw error;
            }
        }
    }

    /**
     * Stop the service
     */
    stop() {
        this.isRunning = false;
        this.chain138Provider.removeAllListeners('block');
        console.log('Service stopped');
    }
}

// Run service if executed directly
if (require.main === module) {
    const service = new StateAnchoringService();
    service.start().catch(console.error);

    // Graceful shutdown
    process.on('SIGINT', () => {
        console.log('\nShutting down...');
        service.stop();
        process.exit(0);
    });
}

export { StateAnchoringService };
