#!/usr/bin/env node
/**
 * Price Feed Keeper Service
 * Automatically updates price feeds at regular intervals
 *
 * Usage:
 *   node scripts/reserve/keeper-service.js
 *
 * Environment Variables:
 *   RPC_URL_138 - ChainID 138 RPC endpoint
 *   KEEPER_PRIVATE_KEY - Keeper wallet private key
 *   PRICE_FEED_KEEPER_ADDRESS - PriceFeedKeeper contract address
 *   UPDATE_INTERVAL - Update interval in seconds (default: 30)
 */

const { ethers } = require('ethers');
const fs = require('fs');
const path = require('path');

// Load environment variables
require('dotenv').config();

// Configuration
const CONFIG = {
    rpcUrl: process.env.RPC_URL_138 || 'http://localhost:8545',
    keeperPrivateKey: process.env.KEEPER_PRIVATE_KEY,
    keeperAddress: process.env.PRICE_FEED_KEEPER_ADDRESS,
    updateInterval: parseInt(process.env.UPDATE_INTERVAL || '6') * 1000, // PMM mesh default cadence
    maxRetries: 3,
    retryDelay: 5000, // 5 seconds
};

// ABI for PriceFeedKeeper
const KEEPER_ABI = [
    "function checkUpkeep() external view returns (bool needsUpdate, address[] memory assets)",
    "function performUpkeep() external returns (bool success, address[] memory updatedAssets)",
    "function updateAssets(address[] calldata assets) external",
    "function getTrackedAssets() external view returns (address[] memory)",
    "function needsUpdate(address asset) external view returns (bool)",
    "function updateInterval() external view returns (uint256)",
    "function maxUpdatesPerCall() external view returns (uint256)",
    "event PriceFeedsUpdated(address[] assets, uint256 timestamp)"
];

class PriceFeedKeeperService {
    constructor() {
        if (!CONFIG.keeperPrivateKey) {
            throw new Error('KEEPER_PRIVATE_KEY environment variable is required');
        }
        if (!CONFIG.keeperAddress) {
            throw new Error('PRICE_FEED_KEEPER_ADDRESS environment variable is required');
        }

        this.provider = new ethers.JsonRpcProvider(CONFIG.rpcUrl);
        this.wallet = new ethers.Wallet(CONFIG.keeperPrivateKey, this.provider);
        this.keeper = new ethers.Contract(CONFIG.keeperAddress, KEEPER_ABI, this.wallet);
        this.isRunning = false;
        this.updateCount = 0;
        this.errorCount = 0;
    }

    async start() {
        console.log('=== Price Feed Keeper Service ===');
        console.log(`RPC URL: ${CONFIG.rpcUrl}`);
        console.log(`Keeper Address: ${CONFIG.keeperAddress}`);
        console.log(`Wallet Address: ${this.wallet.address}`);
        console.log(`Update Interval: ${CONFIG.updateInterval / 1000} seconds`);
        console.log('');

        // Verify keeper contract
        try {
            const trackedAssets = await this.keeper.getTrackedAssets();
            const updateInterval = await this.keeper.updateInterval();
            const maxUpdates = await this.keeper.maxUpdatesPerCall();

            console.log(`Tracked Assets: ${trackedAssets.length}`);
            trackedAssets.forEach((asset, i) => {
                console.log(`  ${i + 1}. ${asset}`);
            });
            console.log(`Update Interval: ${updateInterval.toString()} seconds`);
            console.log(`Max Updates Per Call: ${maxUpdates.toString()}`);
            console.log('');
        } catch (error) {
            console.error('Error verifying keeper contract:', error.message);
            process.exit(1);
        }

        this.isRunning = true;
        console.log('Keeper service started. Press Ctrl+C to stop.');
        console.log('');

        // Start update loop
        this.updateLoop();
    }

    async updateLoop() {
        while (this.isRunning) {
            try {
                await this.performUpkeep();
            } catch (error) {
                console.error(`Error in update loop: ${error.message}`);
                this.errorCount++;
            }

            // Wait for next interval
            await this.sleep(CONFIG.updateInterval);
        }
    }

    async performUpkeep() {
        try {
            // Check if upkeep is needed
            const [needsUpdate, assets] = await this.keeper.checkUpkeep();

            if (!needsUpdate || assets.length === 0) {
                console.log(`[${new Date().toISOString()}] No updates needed`);
                return;
            }

            console.log(`[${new Date().toISOString()}] Updating ${assets.length} asset(s)...`);

            // Perform upkeep
            let retries = 0;
            let success = false;

            while (retries < CONFIG.maxRetries && !success) {
                try {
                    const tx = await this.keeper.performUpkeep();
                    console.log(`  Transaction sent: ${tx.hash}`);

                    const receipt = await tx.wait();
                    if (receipt.status === 1) {
                        success = true;
                        this.updateCount++;

                        // Parse events
                        const events = receipt.logs.filter(log => {
                            try {
                                const parsed = this.keeper.interface.parseLog(log);
                                return parsed.name === 'PriceFeedsUpdated';
                            } catch {
                                return false;
                            }
                        });

                        if (events.length > 0) {
                            const event = this.keeper.interface.parseLog(events[0]);
                            console.log(`  Updated assets: ${event.args.assets.length}`);
                            console.log(`  Timestamp: ${event.args.timestamp.toString()}`);
                        }

                        console.log(`  ✓ Update successful (Gas: ${receipt.gasUsed.toString()})`);
                    } else {
                        throw new Error('Transaction failed');
                    }
                } catch (error) {
                    retries++;
                    if (retries < CONFIG.maxRetries) {
                        console.log(`  Retry ${retries}/${CONFIG.maxRetries}...`);
                        await this.sleep(CONFIG.retryDelay);
                    } else {
                        throw error;
                    }
                }
            }

            if (!success) {
                throw new Error('Failed after retries');
            }

        } catch (error) {
            console.error(`  ✗ Update failed: ${error.message}`);
            this.errorCount++;
            throw error;
        }
    }

    async stop() {
        console.log('\nStopping keeper service...');
        this.isRunning = false;

        console.log('\n=== Statistics ===');
        console.log(`Total Updates: ${this.updateCount}`);
        console.log(`Total Errors: ${this.errorCount}`);
        console.log(`Success Rate: ${this.updateCount > 0 ? ((this.updateCount / (this.updateCount + this.errorCount)) * 100).toFixed(2) : 0}%`);

        process.exit(0);
    }

    sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
}

// Main execution
if (require.main === module) {
    const keeper = new PriceFeedKeeperService();

    // Handle graceful shutdown
    process.on('SIGINT', () => keeper.stop());
    process.on('SIGTERM', () => keeper.stop());

    // Start keeper
    keeper.start().catch(error => {
        console.error('Fatal error:', error);
        process.exit(1);
    });
}

module.exports = PriceFeedKeeperService;

