import { EthAddress } from '@aztec/aztec.js/addresses';
import { Fr } from '@aztec/aztec.js/fields';
import { SecretValue } from '@aztec/foundation/config';
import { Pool } from 'pg';
import { privateKeyToAccount } from 'viem/accounts';
/**
 * Get database configuration from environment variables
 */ export function createHADatabaseConfig(nodeId) {
    const databaseUrl = process.env.DATABASE_URL || 'postgresql://aztec:aztec@localhost:5432/aztec_ha_test';
    return {
        databaseUrl,
        nodeId,
        haSigningEnabled: true,
        pollingIntervalMs: 100,
        signingTimeoutMs: 3000,
        maxStuckDutiesAgeMs: 72000
    };
}
/**
 * Setup PostgreSQL database connection pool for HA tests
 *
 * Note: Database migrations should be run separately before starting tests,
 * either via docker-compose entrypoint or manually with: aztec migrate-ha-db up
 */ export function setupHADatabase(databaseUrl, logger) {
    try {
        // Create connection pool for test usage
        // Migrations are already run by docker-compose entrypoint before tests start
        const pool = new Pool({
            connectionString: databaseUrl
        });
        logger?.info('Connected to HA database (migrations should already be applied)');
        return pool;
    } catch (error) {
        logger?.error(`Failed to connect to HA database: ${error}`);
        throw error;
    }
}
/**
 * Clean up HA database - drop all tables
 * Use this between tests to ensure clean state
 */ export async function cleanupHADatabase(pool, logger) {
    try {
        // Drop all HA tables
        await pool.query('DROP TABLE IF EXISTS validator_duties CASCADE');
        await pool.query('DROP TABLE IF EXISTS schema_version CASCADE');
        // Drop migration tracking table (node-pg-migrate uses 'pgmigrations' by default)
        // This ensures migrations will run fresh on next startup
        await pool.query('DROP TABLE IF EXISTS pgmigrations CASCADE');
        logger?.info('HA database cleaned up successfully');
    } catch (error) {
        logger?.error(`Failed to cleanup HA database: ${error}`);
        throw error;
    }
}
/**
 * Query validator duties from the database
 */ export async function getValidatorDuties(pool, slot, dutyType) {
    const query = dutyType ? 'SELECT slot, duty_type, validator_address, node_id, started_at, completed_at FROM validator_duties WHERE slot = $1 AND duty_type = $2 ORDER BY started_at' : 'SELECT slot, duty_type, validator_address, node_id, started_at, completed_at FROM validator_duties WHERE slot = $1 ORDER BY started_at';
    const params = dutyType ? [
        slot.toString(),
        dutyType
    ] : [
        slot.toString()
    ];
    const result = await pool.query(query, params);
    return result.rows.map((row)=>({
            slot: row.slot,
            dutyType: row.duty_type,
            validatorAddress: row.validator_address,
            nodeId: row.node_id,
            startedAt: row.started_at,
            completedAt: row.completed_at
        }));
}
/**
 * Convert private keys to Ethereum addresses
 */ export function getAddressesFromPrivateKeys(privateKeys) {
    return privateKeys.map((pk)=>{
        const account = privateKeyToAccount(pk);
        return account.address;
    });
}
/**
 * Create initial validators from private keys for L1 contract deployment
 */ export function createInitialValidatorsFromPrivateKeys(attesterPrivateKeys) {
    return attesterPrivateKeys.map((pk)=>{
        const account = privateKeyToAccount(pk);
        return {
            attester: EthAddress.fromString(account.address),
            withdrawer: EthAddress.fromString(account.address),
            privateKey: pk,
            bn254SecretKey: new SecretValue(Fr.random().toBigInt())
        };
    });
}
/**
 * Verify no duplicate attestations per validator (HA coordination check)
 * Groups duties by validator address and verifies each validator attested exactly once
 */ export function verifyNoDuplicateAttestations(attestationDuties, logger) {
    const dutiesByValidator = new Map();
    for (const duty of attestationDuties){
        const existing = dutiesByValidator.get(duty.validatorAddress) || [];
        existing.push(duty);
        dutiesByValidator.set(duty.validatorAddress, existing);
    }
    for (const [validatorAddress, validatorDuties] of dutiesByValidator.entries()){
        if (validatorDuties.length !== 1) {
            throw new Error(`Validator ${validatorAddress} attested ${validatorDuties.length} times (expected exactly once)`);
        }
        if (!validatorDuties[0].completedAt) {
            throw new Error(`Validator ${validatorAddress} attestation duty not completed`);
        }
        logger?.info(`Validator ${validatorAddress} attested once via node ${validatorDuties[0].nodeId}`);
    }
    return dutiesByValidator;
}
