/**
 * @module EthereumContractService
 *
 * Provides a unified service for interacting with Ethereum smart contracts using ethers.js.
 */

// Mainnet artifacts
import MainnetAccountingArtifact from '../../assets/ethereum/mainnet/liqEth/v1/accounting.sol/Accounting.json';
import MainnetDepositManagerArtifact from '../../assets/ethereum/mainnet/liqEth/v1/DepositManager.sol/DepositManager.json';
import MainnetLiqEthArtifact from '../../assets/ethereum/mainnet/liqEth/v1/liqEth.sol/LiqEthToken.json';
import MainnetStakingModuleArtifact from '../../assets/ethereum/mainnet/liqEth/v1/stakingModule.sol/StakingModule.json';
import MainnetWithdrawalQueueArtifact from '../../assets/ethereum/mainnet/liqEth/withdrawalQueue.sol/WithdrawalQueue.json';
import MainnetWithdrawalVaultArtifact from '../../assets/ethereum/mainnet/liqEth/withdrawalVault.sol/WithdrawalVault.json';
import MainnetBeaconStateArtifact from '../../assets/ethereum/mainnet/liqEth/BeaconState.sol/BeaconState.json';
import MainnetYieldOracleArtifact from '../../assets/ethereum/mainnet/liqEth/Yield.sol/YieldOracle.json';
import MainnetDepositorArtifact from '../../assets/ethereum/mainnet/outpost/Depositor.sol/DepositorV2.json';
import MainnetReceiptNFTArtifact from '../../assets/ethereum/mainnet/outpost/ReceiptNFT.sol/ReceiptNFT.json';
import MainnetOutpostManagerArtifact from '../../assets/ethereum/mainnet/outpost/OutpostManager.sol/OutpostManagerV2.json';
import MainnetBARArtifact from '../../assets/ethereum/mainnet/outpost/BAR.sol/BARV2.json';
import MainnetOPPArtifact from '../../assets/ethereum/mainnet/outpost/OPP.sol/OPP.json';
import MainnetOPPCommonArtifact from '../../assets/ethereum/mainnet/outpost/OPPCommon.sol/OPPCommon.json';
import MainnetOPPInboundArtifact from '../../assets/ethereum/mainnet/outpost/OPPInbound.sol/OPPInbound.json';
import MainnetPretokenArtifact from '../../assets/ethereum/mainnet/outpost/Pretoken.sol/PretokenV2.json';
import MainnetEthUsdPriceConsumerArtifact from '../../assets/ethereum/mainnet/outpost/EthUsdPriceConsumer.sol/EthUsdPriceConsumer.json';
import MainnetPoolArtifact from '../../assets/ethereum/mainnet/outpost/Pool.sol/PoolV2.json';
import MainnetOutpostManagerAuthorityArtifact from '../../assets/ethereum/mainnet/outpost/OutpostManagerAuthority.sol/OutpostManagerAuthority.json';
import MainnetIodataArtifact from '../../assets/ethereum/mainnet/common/iodata.sol/iodata.json';
import MainnetBase58Artifact from '../../assets/ethereum/mainnet/common/Base58.sol/Base58.json';
import MainnetSysioMerkleArtifact from '../../assets/ethereum/mainnet/common/sysio_merkle.sol/sysio_merkle.json';
import MainnetSysioWriteArtifact from '../../assets/ethereum/mainnet/common/sysio_write.sol/sysio_write.json';
import MainnetERC20Artifact from '../../assets/ethereum/mainnet/token/ERC20Token.sol/ERC20Token.json';
import MainnetERC721Artifact from '../../assets/ethereum/mainnet/token/ERC721Token.sol/ERC721Token.json';
import MainnetERC1155Artifact from '../../assets/ethereum/mainnet/token/ERC1155Token.sol/ERC1155Token.json';

// Hoodi artifacts
import HoodiAccountingArtifact from '../../assets/ethereum/hoodi/liqEth/v1/accounting.sol/Accounting.json';
import HoodiDepositManagerArtifact from '../../assets/ethereum/hoodi/liqEth/v1/DepositManager.sol/DepositManager.json';
import HoodiLiqEthArtifact from '../../assets/ethereum/hoodi/liqEth/v1/liqEth.sol/LiqEthToken.json';
import HoodiStakingModuleArtifact from '../../assets/ethereum/hoodi/liqEth/v1/stakingModule.sol/StakingModule.json';
import HoodiWithdrawalQueueArtifact from '../../assets/ethereum/hoodi/liqEth/withdrawalQueue.sol/WithdrawalQueue.json';
import HoodiWithdrawalVaultArtifact from '../../assets/ethereum/hoodi/liqEth/withdrawalVault.sol/WithdrawalVault.json';
import HoodiBeaconStateArtifact from '../../assets/ethereum/hoodi/liqEth/BeaconState.sol/BeaconState.json';
import HoodiYieldOracleArtifact from '../../assets/ethereum/hoodi/liqEth/Yield.sol/YieldOracle.json';
import HoodiDepositorArtifact from '../../assets/ethereum/hoodi/outpost/Depositor.sol/Depositor.json';
import HoodiReceiptNFTArtifact from '../../assets/ethereum/hoodi/outpost/ReceiptNFT.sol/ReceiptNFT.json';
import HoodiOutpostManagerArtifact from '../../assets/ethereum/hoodi/outpost/OutpostManager.sol/OutpostManager.json';
import HoodiBARArtifact from '../../assets/ethereum/hoodi/outpost/BAR.sol/BAR.json';
import HoodiOPPArtifact from '../../assets/ethereum/hoodi/outpost/OPP.sol/OPP.json';
import HoodiOPPCommonArtifact from '../../assets/ethereum/hoodi/outpost/OPPCommon.sol/OPPCommon.json';
import HoodiOPPInboundArtifact from '../../assets/ethereum/hoodi/outpost/OPPInbound.sol/OPPInbound.json';
import HoodiPretokenArtifact from '../../assets/ethereum/hoodi/outpost/Pretoken.sol/Pretoken.json';
import HoodiEthUsdPriceConsumerArtifact from '../../assets/ethereum/hoodi/outpost/EthUsdPriceConsumer.sol/EthUsdPriceConsumer.json';
import HoodiPoolArtifact from '../../assets/ethereum/hoodi/outpost/Pool.sol/Pool.json';
import HoodiOutpostManagerAuthorityArtifact from '../../assets/ethereum/hoodi/outpost/OutpostManagerAuthority.sol/OutpostManagerAuthority.json';
import HoodiIodataArtifact from '../../assets/ethereum/hoodi/common/iodata.sol/iodata.json';
import HoodiBase58Artifact from '../../assets/ethereum/hoodi/common/Base58.sol/Base58.json';
import HoodiSysioMerkleArtifact from '../../assets/ethereum/hoodi/common/sysio_merkle.sol/sysio_merkle.json';
import HoodiSysioWriteArtifact from '../../assets/ethereum/hoodi/common/sysio_write.sol/sysio_write.json';
import HoodiERC20Artifact from '../../assets/ethereum/hoodi/token/ERC20Token.sol/ERC20Token.json';
import HoodiERC721Artifact from '../../assets/ethereum/hoodi/token/ERC721Token.sol/ERC721Token.json';
import HoodiERC1155Artifact from '../../assets/ethereum/hoodi/token/ERC1155Token.sol/ERC1155Token.json';

import { AddressBook, ContractName } from './types';
import { Contract, ethers, providers, Signer } from 'ethers';
import { JsonFragment } from '@ethersproject/abi';
import { ErrorDescription } from '@ethersproject/abi/lib/interface';
import { EvmChainID } from '@wireio/core';
import { SupportedEvmChainID } from '../../types';

export const ERC20Abi = MainnetERC20Artifact.abi;
export const ERC721Abi = MainnetERC721Artifact.abi;
export const ERC1155Abi = MainnetERC1155Artifact.abi;

type AbiArtifact = { abi: JsonFragment[] };

type ArtifactBundle = {
    Accounting: AbiArtifact;
    DepositManager: AbiArtifact;
    LiqEthToken: AbiArtifact;
    StakingModule: AbiArtifact;
    WithdrawalQueue: AbiArtifact;
    WithdrawalVault: AbiArtifact;
    BeaconState: AbiArtifact;
    YieldOracle: AbiArtifact;
    Depositor: AbiArtifact;
    ReceiptNFT: AbiArtifact;
    OutpostManager: AbiArtifact;
    BAR: AbiArtifact;
    OPP: AbiArtifact;
    OPPCommon: AbiArtifact;
    OPPInbound: AbiArtifact;
    Pretoken: AbiArtifact;
    EthUsdPriceConsumer: AbiArtifact;
    Pool: AbiArtifact;
    OutpostManagerAuthority: AbiArtifact;
    iodata: AbiArtifact;
    Base58: AbiArtifact;
    sysio_merkle: AbiArtifact;
    sysio_write: AbiArtifact;
    ERC20: AbiArtifact;
    ERC721: AbiArtifact;
    ERC1155: AbiArtifact;
};

const MAINNET_ARTIFACTS: ArtifactBundle = {
    Accounting: MainnetAccountingArtifact as AbiArtifact,
    DepositManager: MainnetDepositManagerArtifact as AbiArtifact,
    LiqEthToken: MainnetLiqEthArtifact as AbiArtifact,
    StakingModule: MainnetStakingModuleArtifact as AbiArtifact,
    WithdrawalQueue: MainnetWithdrawalQueueArtifact as AbiArtifact,
    WithdrawalVault: MainnetWithdrawalVaultArtifact as AbiArtifact,
    BeaconState: MainnetBeaconStateArtifact as AbiArtifact,
    YieldOracle: MainnetYieldOracleArtifact as AbiArtifact,
    Depositor: MainnetDepositorArtifact as AbiArtifact,
    ReceiptNFT: MainnetReceiptNFTArtifact as AbiArtifact,
    OutpostManager: MainnetOutpostManagerArtifact as AbiArtifact,
    BAR: MainnetBARArtifact as AbiArtifact,
    OPP: MainnetOPPArtifact as AbiArtifact,
    OPPCommon: MainnetOPPCommonArtifact as AbiArtifact,
    OPPInbound: MainnetOPPInboundArtifact as AbiArtifact,
    Pretoken: MainnetPretokenArtifact as AbiArtifact,
    EthUsdPriceConsumer: MainnetEthUsdPriceConsumerArtifact as AbiArtifact,
    Pool: MainnetPoolArtifact as AbiArtifact,
    OutpostManagerAuthority: MainnetOutpostManagerAuthorityArtifact as AbiArtifact,
    iodata: MainnetIodataArtifact as AbiArtifact,
    Base58: MainnetBase58Artifact as AbiArtifact,
    sysio_merkle: MainnetSysioMerkleArtifact as AbiArtifact,
    sysio_write: MainnetSysioWriteArtifact as AbiArtifact,
    ERC20: MainnetERC20Artifact as AbiArtifact,
    ERC721: MainnetERC721Artifact as AbiArtifact,
    ERC1155: MainnetERC1155Artifact as AbiArtifact,
};

const HOODI_ARTIFACTS: ArtifactBundle = {
    Accounting: HoodiAccountingArtifact as AbiArtifact,
    DepositManager: HoodiDepositManagerArtifact as AbiArtifact,
    LiqEthToken: HoodiLiqEthArtifact as AbiArtifact,
    StakingModule: HoodiStakingModuleArtifact as AbiArtifact,
    WithdrawalQueue: HoodiWithdrawalQueueArtifact as AbiArtifact,
    WithdrawalVault: HoodiWithdrawalVaultArtifact as AbiArtifact,
    BeaconState: HoodiBeaconStateArtifact as AbiArtifact,
    YieldOracle: HoodiYieldOracleArtifact as AbiArtifact,
    Depositor: HoodiDepositorArtifact as AbiArtifact,
    ReceiptNFT: HoodiReceiptNFTArtifact as AbiArtifact,
    OutpostManager: HoodiOutpostManagerArtifact as AbiArtifact,
    BAR: HoodiBARArtifact as AbiArtifact,
    OPP: HoodiOPPArtifact as AbiArtifact,
    OPPCommon: HoodiOPPCommonArtifact as AbiArtifact,
    OPPInbound: HoodiOPPInboundArtifact as AbiArtifact,
    Pretoken: HoodiPretokenArtifact as AbiArtifact,
    EthUsdPriceConsumer: HoodiEthUsdPriceConsumerArtifact as AbiArtifact,
    Pool: HoodiPoolArtifact as AbiArtifact,
    OutpostManagerAuthority: HoodiOutpostManagerAuthorityArtifact as AbiArtifact,
    iodata: HoodiIodataArtifact as AbiArtifact,
    Base58: HoodiBase58Artifact as AbiArtifact,
    sysio_merkle: HoodiSysioMerkleArtifact as AbiArtifact,
    sysio_write: HoodiSysioWriteArtifact as AbiArtifact,
    ERC20: HoodiERC20Artifact as AbiArtifact,
    ERC721: HoodiERC721Artifact as AbiArtifact,
    ERC1155: HoodiERC1155Artifact as AbiArtifact,
};

// -----------------------------------------------------------------------------
// Address books (per-chain)
// -----------------------------------------------------------------------------

/**
 * MAINNET LATEST
 * 1/23/26
 */
export const MAINNET_ADDRESSES: AddressBook = {
    // LiqETH contracts
    LiqEthAuthority: '0xB8fb7dBA2de501c94EAB723fa48679f698a39Aa4',
    BeaconState: '0x29719E8e76C52F5fD4888DF401579859c305f2DF',
    WithdrawalQueue: '0xce388339bFDAed9cAF536ea7b4F42123b4D755d4',
    LiqEthToken: '0x2C0e75e930Fc72ceD0725854C5E7A55566333F3f',
    Accounting: '0xE31C44d04c7B055afAC9301F55840f030eaF04BC',
    DepositManager: '0x21b339ED55A0f037f91B88044C808CcfCCBbF9A8',
    WithdrawalVault: '0x028743bE7E727aA331A1e952575cb00623B95fE2',
    StakingModule: '0xDe9754Fb58CcD4D74e94DA5682192E4e6eC334b0',
    // YieldOracle: '0x7B7B8894e75b44B6cA7Dc8bE6dED5059621907b2',

    // Outpost contracts
    OutpostManagerAuthority: '0xeaE32b94c0F858b3600861aAE4Caeee6B6341751',
    iodata: '0x485Ed153c45189d623Bb37816eD3016A56552273',
    Base58: '0x8bB3856EC1114B1cE45a78620CF174D90a1Fd4e7',
    sysio_merkle: '0x5180a926D71c6d94c5c939Fe7Fed14cD7dcAa541',
    ReceiptNFT: '0x651De616A20b17De1095f9A0694A0e0E68025b78',
    EthUsdPriceConsumer: '0x2d866f8DB6F6c94997b0027Ba24bc64a03627ddc',
    Pool: '0xDA268Adcb90c422C2102A5f713182A42A8b9EA0D',
    OutpostManager: '0x9f279804Cb8539c53709AA37a4C1b30A52A85175',
    sysio_write: '0x3F8c880efd2c8C3aA1f0CE40b886cc6913eEC1d3',
    Pretoken: '0xCabF19Fdb7cD063738dd63a418702Ee3642FFc68',
    BAR: '0x2D8f4D4435509d7b8F93C3Ba93D45447C892f7ED',
    OPPCommon: '0x1716EEC9f1E0c1aF7BbbA8Fed10652138914f766',
    OPP: '0x5e1135cF8987268a3e7F68551558Ae92c454429f',
    Depositor: '0xf4b4ccfD684feB46504D77232FBde23b23d4F774',
    OPPInbound: '0x088484d45fF93AFabC63810FB0b335792E2B24f3',

    // MockAggregator is not deployed on mainnet; keep as zero-address placeholder
    // MockAggregator: '0x0000000000000000000000000000000000000000',
};

/**
 * ---HOODI TESTNET LATEST
 * ---3/25/26
 * 
 * TEMPORARILY LOCAL ETH WIRE DEVNET ADDRESSES 
 * 4/10/26
 * 
 */
export const HOODI_ADDRESSES: AddressBook = {
    // WIRE ETH DEVNET - AWS deployment 4/23/26
    // LiqETH contracts
    LiqEthAuthority: "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9",
    BeaconState: "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707",
    WithdrawalQueue: "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853",
    LiqEthToken: "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318",
    Accounting: "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e",
    DepositManager: "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82",
    WithdrawalVault: "0x0B306BF915C4d645ff596e518fAf3F9669b97016",
    StakingModule: "0x9A9f2CCfdE556A7E9Ff0848998Aa4a0CFD8863AE",
    // YieldOracle: "0x3Aa5ebB10DC797CAC828524e59A333d0A371443c"

    // Outpost contracts
    OutpostManagerAuthority: "0x172076E0166D1F9Cc711C77Adf8488051744980C",
    iodata: '0x4aCABbC74a1cA9B0058d9ADAa0E00Af0f4f73212',   // not provided in outpost-addrs.json, re-using previous value
    Base58: '0x1241F36ff30065deD52d51f0d6524EF971259A12',   // not provided in outpost-addrs.json, re-using previous value
    OPPCommon: "0x4EE6eCAD1c2Dae9f525404De8555724e3c35d07B",
    sysio_merkle: "0xBEc49fA140aCaA83533fB00A2BB19bDdd0290f25",
    OPP: "0x2B0d36FACD61B71CC05ab8F3D2355ec3631C0dd5",
    ReceiptNFT: "0x46b142DD1E924FAb83eCc3c08e4D46E82f005e0E",
    // MockAggregator: "0x1c85638e118b37167e9298c2268758e058DdfDA0",
    Pool: "0x4C2F7092C2aE51D986bEFEe378e50BD4dB99C901",
    // WireYieldHubAttestations: "0x7A9Ec1d04904907De0ED7b6839CcdD59c3716AC9",
    // LiqEthBridge: "0x4631BCAbD6dF18D94796344963cB60d44a4136b6",
    BAR: "0xA4899D35897033b927acFCf422bc745916139776",
    // OperatorRegistry: "0xAA292E8611aDF267e563f334Ee42320aC96D0463",
    OutpostManager: "0x720472c8ce72c2A2D711333e064ABD3E6BbEAdd3",
    sysio_write: '0x0Ec4C1BaB772958aa3Db27459643bfbE7d92E305',    // not provided in outpost-addrs.json, re-using previous value
    OPPInbound: "0x5067457698Fd6Fa1C6964e416b3f42713513B3dD",
    EthUsdPriceConsumer: "0x18E317A7D70d8fBf8e6E893616b52390EbBdb629",
    Pretoken: "0xCace1b78160AE76398F486c8a18044da0d66d86D",
    Depositor: "0xF8e31cb472bc70500f08Cd84917E5A1912Ec8397"
    


    // LiqETH contracts
    // LiqEthAuthority: '0x3Eb66Bb91F7a022FbABc1619Cb6acce7025Bc41F',
    // BeaconState: '0x08F879c6D0964E0A17bF688Af23168f654DF372e',
    // WithdrawalQueue: '0x74aDeD940369baDDb0F36426F23B08499e45Fe18',
    // LiqEthToken: '0x8C974517D038e2e3b8a1da7b1A026bC8132A4b2e',
    // Accounting: '0x766CE996B8132b927988c5b2ECAE8108fB1a9307',
    // DepositManager: '0xEd391dB5DB11B5eDBE08e69A51D16e84293C09a3',
    // WithdrawalVault: '0xbaBc0D5997283ab9886696A0dEaB8A8093436b48',
    // StakingModule: '0x4998fBb6Ca5Ee3Db6c339e2962A34791Af643fEa',
    // // YieldOracle: '0x61Fe311C57dd9a38Fb7B0c5A8ec521452dB25378',

    // // Outpost contracts
    // OutpostManagerAuthority: '0x3B8705Ba1195AF632b3bd9E46F19348BC828FA3D',
    // iodata: '0x4aCABbC74a1cA9B0058d9ADAa0E00Af0f4f73212',
    // Base58: '0x1241F36ff30065deD52d51f0d6524EF971259A12',
    // sysio_merkle: '0x58ca9b5515266Fd6bF94D5F1268C505b2eC9679c',
    // ReceiptNFT: '0x03d6bD46cA9F54B8F8bbA21CfAfe717b36cB8593',
    // EthUsdPriceConsumer: '0xdC2DD993A13b3DD6D255B52285625694bd967c9A',
    // Pool: '0xd4742757AA61B1Fd3Fcd87980D7D460B12Cd9275',
    // OutpostManager: '0x38ec2B56cBCeCaFFe28b535517de1a102C3D74E3',
    // sysio_write: '0x0Ec4C1BaB772958aa3Db27459643bfbE7d92E305',
    // Pretoken: '0x8c97a51A71640fa411DEfab8feC3401eFfd1a2E4',
    // BAR: '0xbAFac13ffd3Aa9aEabaaeb6e4C2cF3dFf3479DC0',
    // OPPCommon: '0x12283a6d4E6cE4A0e8AdB319Fc161c7F4f91EA8F',
    // OPP: '0x87CE3Fe2CDca39C04aa2c14bbF1E1d9DB6358C4A',
    // Depositor: '0x5351B30EA4b75bD9be9Fc0048e88F675f7b3Ac2b',
    // OPPInbound: '0x0B1f7FB1c7f01A9cf490a1387E1E100F2bEA6C6d',

    // Test-only mock aggregator
    // MockAggregator: '0xFCfc3ddd4CBd9Ad3b3af3A374B8bdA1b66eE6FFF',

    // TEMPORARY WIRE ETH DEVNET ADDRESSES - 4/21/26
    // LiqETH contracts
    // LiqEthAuthority: '0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512',
    // BeaconState: '0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9',
    // WithdrawalQueue: '0x5FC8d32690cc91D4c39d9d3abcBD16989F875707',
    // LiqEthToken: '0xa513E6E4b8f2a923D98304ec87F64353C4D5C853',
    // Accounting: '0x8A791620dd6260079BF849Dc5567aDC3F2FdC318',
    // DepositManager: '0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e',
    // WithdrawalVault: '0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82',
    // StakingModule: '0x0B306BF915C4d645ff596e518fAf3F9669b97016',
    // // YieldOracle: '0x9A9f2CCfdE556A7E9Ff0848998Aa4a0CFD8863AE',

    // // Outpost contracts
    // OutpostManagerAuthority: '0x202CCe504e04bEd6fC0521238dDf04Bc9E8E15aB',
    // iodata: '0x4aCABbC74a1cA9B0058d9ADAa0E00Af0f4f73212',
    // Base58: '0x1241F36ff30065deD52d51f0d6524EF971259A12',
    // sysio_merkle: '0x172076E0166D1F9Cc711C77Adf8488051744980C',
    // ReceiptNFT: '0x2B0d36FACD61B71CC05ab8F3D2355ec3631C0dd5',
    // EthUsdPriceConsumer: '0xe8D2A1E88c91DCd5433208d4152Cc4F399a7e91d',
    // Pool: '0x1c85638e118b37167e9298c2268758e058DdfDA0',
    // OutpostManager: '0xAA292E8611aDF267e563f334Ee42320aC96D0463',
    // sysio_write: '0x0Ec4C1BaB772958aa3Db27459643bfbE7d92E305',
    // Pretoken: '0x18E317A7D70d8fBfE6E893616b52390EbBdb629',
    // BAR: '0x4631BCAbD6dF18D94796344963cB60d44a4136b6',
    // OPPCommon: '0xf4B146FbA71F41E0592668ffbF264F1D186b2Ca8',
    // OPP: '0xBEc49fA140aCaA83533fB00A2BB19bDdd0290f25',
    // Depositor: '0xCace1b78160AE76398F486c8a18044da0d66d86D',
    // OPPInbound: '0x720472c8ce72c2A2D711333e064ABD3E6BbEAdd3',


    // MockDepositContract: '0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0',
    // LiqEthBridge: '0x7A9Ec1d04904907De0ED7b6839CcdD59c3716AC9', internal
    // WireYieldHubAttestations: '0x367761085BF3C12e5DA2Df99AC6E1a824612b8fb', internal

    // Test-only mock aggregator
    // MockAggregator: '0x46b142DD1E924FAb83eCc3c08e4D46E82f005e0E',
};

export const ADDRESS_BOOK_BY_CHAIN: Record<SupportedEvmChainID, AddressBook> = {
    [EvmChainID.Ethereum]: MAINNET_ADDRESSES,
    [EvmChainID.Hoodi]: HOODI_ADDRESSES,
    [31337]: HOODI_ADDRESSES,
};

// Old name kept as a mainnet default (optional convenience)
export const ADDRESSES: AddressBook = MAINNET_ADDRESSES;

// -----------------------------------------------------------------------------
// Contract config
// -----------------------------------------------------------------------------

export type ContractConfig = {
    address: string;
    abi: JsonFragment[];
};

export type Contracts<T extends string = ContractName> = Record<T, ContractConfig>;

function buildContracts(addresses: AddressBook, artifacts: ArtifactBundle): Contracts<ContractName> {
    return {
        // LiqETH Contracts
        LiqEthAuthority: {
            address: addresses.LiqEthAuthority,
            abi: artifacts.LiqEthToken.abi as JsonFragment[],
        },
        BeaconState: {
            address: addresses.BeaconState,
            abi: artifacts.BeaconState.abi as JsonFragment[],
        },
        WithdrawalQueue: {
            address: addresses.WithdrawalQueue,
            abi: artifacts.WithdrawalQueue.abi as JsonFragment[],
        },
        LiqEthToken: {
            address: addresses.LiqEthToken,
            abi: artifacts.LiqEthToken.abi as JsonFragment[],
        },
        Accounting: {
            address: addresses.Accounting,
            abi: artifacts.Accounting.abi as JsonFragment[],
        },
        DepositManager: {
            address: addresses.DepositManager,
            abi: artifacts.DepositManager.abi as JsonFragment[],
        },
        WithdrawalVault: {
            address: addresses.WithdrawalVault,
            abi: artifacts.WithdrawalVault.abi as JsonFragment[],
        },
        StakingModule: {
            address: addresses.StakingModule,
            abi: artifacts.StakingModule.abi as JsonFragment[],
        },
        // YieldOracle: {
        //     address: addresses.YieldOracle,
        //     abi: artifacts.YieldOracle.abi as JsonFragment[],
        // },

        // Outpost Contracts
        OutpostManagerAuthority: {
            address: addresses.OutpostManagerAuthority,
            abi: artifacts.OutpostManagerAuthority.abi as JsonFragment[],
        },
        iodata: {
            address: addresses.iodata,
            abi: artifacts.iodata.abi as JsonFragment[],
        },
        Base58: {
            address: addresses.Base58,
            abi: artifacts.Base58.abi as JsonFragment[],
        },
        sysio_merkle: {
            address: addresses.sysio_merkle,
            abi: artifacts.sysio_merkle.abi as JsonFragment[],
        },
        ReceiptNFT: {
            address: addresses.ReceiptNFT,
            abi: artifacts.ReceiptNFT.abi as JsonFragment[],
        },
        Pool: {
            address: addresses.Pool,
            abi: artifacts.Pool.abi as JsonFragment[],
        },
        OutpostManager: {
            address: addresses.OutpostManager,
            abi: artifacts.OutpostManager.abi as JsonFragment[],
        },
        sysio_write: {
            address: addresses.sysio_write,
            abi: artifacts.sysio_write.abi as JsonFragment[],
        },
        EthUsdPriceConsumer: {
            address: addresses.EthUsdPriceConsumer,
            abi: artifacts.EthUsdPriceConsumer.abi as JsonFragment[],
        },
        BAR: {
            address: addresses.BAR,
            abi: artifacts.BAR.abi as JsonFragment[],
        },
        OPPCommon: {
            address: addresses.OPPCommon,
            abi: artifacts.OPPCommon.abi as JsonFragment[],
        },
        OPP: {
            address: addresses.OPP,
            abi: artifacts.OPP.abi as JsonFragment[],
        },
        Pretoken: {
            address: addresses.Pretoken,
            abi: artifacts.Pretoken.abi as JsonFragment[],
        },
        OPPInbound: {
            address: addresses.OPPInbound,
            abi: artifacts.OPPInbound.abi as JsonFragment[],
        },
        Depositor: {
            address: addresses.Depositor,
            abi: artifacts.Depositor.abi as JsonFragment[],
        },

        // MockAggregator: {
        //     address: addresses.MockAggregator,
        //     abi: artifacts.Aggregator.abi as JsonFragment[],
        // },
    };
}

export const CONTRACTS_BY_CHAIN: Record<SupportedEvmChainID, Contracts<ContractName>> = {
    [EvmChainID.Ethereum]: buildContracts(MAINNET_ADDRESSES, MAINNET_ARTIFACTS),
    [EvmChainID.Hoodi]: buildContracts(HOODI_ADDRESSES, HOODI_ARTIFACTS),
    [31337]: buildContracts(HOODI_ADDRESSES, HOODI_ARTIFACTS),
};

// -----------------------------------------------------------------------------
// Service
// -----------------------------------------------------------------------------

export interface ContractOptions {
    /** EVM chain id to select the correct address/contract set */
    chainId: EvmChainID;
    /** RPC endpoint or injected EIP-1193 provider */
    provider?: ethers.providers.Provider;
    /** Only required if you need to send txns */
    signer?: Signer;
}

const ARTIFACTS_BY_CHAIN: Record<SupportedEvmChainID, ArtifactBundle> = {
    [EvmChainID.Ethereum]: MAINNET_ARTIFACTS,
    [EvmChainID.Hoodi]: HOODI_ARTIFACTS,
    [31337]: HOODI_ARTIFACTS,
};

export class EthereumContractService {
    private provider?: providers.Provider;
    private signer?: Signer;
    private readonly chainId: EvmChainID;
    private readonly contractsConfig: Contracts<ContractName>;

    public readonly contract: { [N in ContractName]: ethers.Contract } = {} as any;
    public readonly iface: { [N in ContractName]: ethers.utils.Interface } = {} as any;

    constructor(opts: ContractOptions) {
        this.provider = opts.provider;
        this.signer = opts.signer;
        this.chainId = opts.chainId;

        const contracts = CONTRACTS_BY_CHAIN[this.chainId];
        if (!contracts) {
            throw new Error(`Unsupported EVM chainId=${this.chainId}`);
        }
        this.contractsConfig = contracts;

        (Object.keys(contracts) as ContractName[]).forEach((name) => {
            const { address, abi } = contracts[name];
            this.iface[name] = new ethers.utils.Interface(abi);
            this.contract[name] = new ethers.Contract(
                address,
                abi,
                this.signer ?? this.provider,
            );
        });
    }

    /** Get raw address for a contract */
    public getAddress(name: ContractName): string {
        return this.contractsConfig[name].address;
    }

    /** Utility: get a read-only handle even if signer is present */
    public getReadOnly(name: ContractName): Contract {
        const { address, abi } = this.contractsConfig[name];
        return new Contract(address, abi, this.provider);
    }

    /** Utility: get a write-enabled handle (throws if no signer) */
    public getWrite(name: ContractName): Contract {
        if (!this.signer) throw new Error('No signer available for write calls');
        const { address, abi } = this.contractsConfig[name];
        return new Contract(address, abi, this.signer);
    }

    /** Expose network info */
    async getNetwork() {
        return this.provider?.getNetwork();
    }

    /** List all known contract names for this chain */
    public listContractNames(): ContractName[] {
        return Object.keys(this.contractsConfig) as ContractName[];
    }

    /** Get Interface instance for a contract */
    public getInterface(name: ContractName): ethers.utils.Interface {
        const iface = this.iface[name];
        if (!iface) throw new Error(`Interface not initialized for ${name}`);
        return iface;
    }

    /** Get live Contract instance */
    public getContract(name: ContractName): ethers.Contract {
        const ctr = this.contract[name];
        if (!ctr) throw new Error(`Contract not initialized for ${name}`);
        return ctr;
    }

    /** A unified Interface containing all ABIs to parse custom errors or events. */
    public get omniInterface(): ethers.utils.Interface {
        const all: any[] = [];

        const artifacts : ArtifactBundle = ARTIFACTS_BY_CHAIN[this.chainId];
        const allArtifacts = Object.values(artifacts);

        allArtifacts.forEach((artifact) => {
            if (artifact && artifact.abi) {
                all.push(...artifact.abi);
            }
        });

        const seen = new Set<string>();
        const dedup: any[] = [];
        for (const item of all) {
            try {
                const frag = ethers.utils.Fragment.from(item);
                const key = `${frag.type}:${frag.format(
                    ethers.utils.FormatTypes.full,
                )}`.toLowerCase();
                if (!seen.has(key)) {
                    seen.add(key);
                    dedup.push(item);
                }
            } catch {
                // ignore bad fragments
            }
        }

        return new ethers.utils.Interface(dedup);
    }

    /** Decode a revert or custom error payload */
    public parseError(revertData: string): ErrorDescription {
        try {
            return this.omniInterface.parseError(revertData);
        } catch {
            throw new Error(`Unable to parse error data: ${revertData}`);
        }
    }
}
