// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {Script, console} from "forge-std/Script.sol";
import {WETH} from "../contracts/tokens/WETH.sol";
import {WETH10} from "../contracts/tokens/WETH10.sol";
import {CCIPWETH9Bridge} from "../contracts/ccip/CCIPWETH9Bridge.sol";
import {CCIPWETH10Bridge} from "../contracts/ccip/CCIPWETH10Bridge.sol";

/**
 * @title DeployAll - Canonical Multichain Deployment Script
 * @notice Deploys all contracts (WETH9, WETH10, CCIP Bridges, CCIPLogger) to multiple chains
 * @dev Chain-aware deployment that adapts to different network configurations
 *
 * Supported Chains:
 * - Ethereum Mainnet (chainId 1): Only deploys CCIPLogger (other contracts already deployed)
 * - Cronos (chainId 25): Deploys all contracts
 * - BSC (chainId 56): Deploys all contracts
 * - Polygon PoS (chainId 137): Deploys all contracts
 * - Gnosis Chain (chainId 100): Deploys all contracts
 * - Avalanche (chainId 43114): Deploys all contracts
 * - Base (chainId 8453): Deploys all contracts
 * - Arbitrum (chainId 42161): Deploys all contracts
 * - Optimism (chainId 10): Deploys all contracts
 * - Monad (chainId 143): Deploys all contracts (WMON as wrapped native; native MON CCIP fees if LINK unset)
 */
contract DeployAll is Script {
    // Chain IDs
    uint256 constant MAINNET = 1;
    uint256 constant CRONOS = 25;
    uint256 constant BSC = 56;
    uint256 constant POLYGON = 137;
    uint256 constant GNOSIS = 100;
    uint256 constant AVALANCHE = 43114;
    uint256 constant BASE = 8453;
    uint256 constant ARBITRUM = 42161;
    uint256 constant OPTIMISM = 10;
    uint256 constant MONAD = 143;

    // Canonical wrapped native on Monad mainnet (WMON)
    address constant WMON_MAINNET = 0x754704Bc059F8C67012fEd69BC8A327a5aafb603;
    // Chainlink CCIP router on Monad mainnet
    address constant CCIP_ROUTER_MONAD_DEFAULT = 0x33566fE5976AAa420F3d5C64996641Fc3858CaDB;
    uint64 constant MONAD_SELECTOR_DEFAULT = 8481857512324358265;

    // CCIP Configuration per chain
    struct CCIPConfig {
        address router;
        address linkToken;
        uint64 chainSelector;
    }

    // WETH Configuration per chain
    struct WETHConfig {
        address weth9;
        address weth10;
    }

    // Deployment addresses (will be populated during deployment)
    struct DeploymentAddresses {
        address weth9;
        address weth10;
        address ccipWETH9Bridge;
        address ccipWETH10Bridge;
        address ccipLogger;
    }

    DeploymentAddresses public deployed;

    function run() external {
        uint256 chainId = block.chainid;
        // Get deployer address - when using --private-key flag, use msg.sender
        // Otherwise get from env (PRIVATE_KEY should have 0x prefix in .env)
        address deployer;
        try vm.envUint("PRIVATE_KEY") returns (uint256 pk) {
            deployer = vm.addr(pk);
        } catch {
            // If env read fails, use msg.sender (when --private-key is used from CLI)
            deployer = msg.sender;
        }

        console.log("==========================================");
        console.log("Multichain Deployment Script");
        console.log("==========================================");
        console.log("Chain ID:", chainId);
        console.log("Deployer:", deployer);
        console.log("Deployer Balance:", deployer.balance / 1e18, "ETH/Native");

        // Get chain-specific configuration
        CCIPConfig memory ccipConfig = getCCIPConfig(chainId);
        WETHConfig memory wethConfig = getWETHConfig(chainId);

        console.log("\nCCIP Configuration:");
        console.log("  Router:", ccipConfig.router);
        console.log("  LINK Token:", ccipConfig.linkToken);
        console.log("  Chain Selector:", ccipConfig.chainSelector);

        console.log("\nWETH Configuration:");
        console.log("  WETH9:", wethConfig.weth9);
        console.log("  WETH10:", wethConfig.weth10);

        // startBroadcast() will use --private-key from CLI if provided
        // Otherwise it will try to use PRIVATE_KEY from env
        vm.startBroadcast();

        // Determine deployment strategy based on chain
        if (chainId == MAINNET) {
            // Ethereum Mainnet: Only deploy CCIPLogger
            console.log("\n=== Ethereum Mainnet Deployment ===");
            console.log("Skipping WETH9, WETH10, and Bridges (already deployed)");
            console.log("Deploying CCIPLogger only...");

            deployed.ccipLogger = deployCcipLogger(ccipConfig);
        } else {
            // All other chains: Deploy everything
            console.log("\n=== Full Deployment for Chain", chainId, "===");

            // Deploy WETH9 if needed
            if (wethConfig.weth9 == address(0)) {
                console.log("\nDeploying WETH9...");
                deployed.weth9 = deployWETH9();
            } else {
                console.log("\nUsing existing WETH9 at:", wethConfig.weth9);
                deployed.weth9 = wethConfig.weth9;
            }

            // Deploy WETH10 if needed
            if (wethConfig.weth10 == address(0)) {
                console.log("\nDeploying WETH10...");
                deployed.weth10 = deployWETH10();
            } else {
                console.log("\nUsing existing WETH10 at:", wethConfig.weth10);
                deployed.weth10 = wethConfig.weth10;
            }

            // Deploy CCIP Bridges
            console.log("\nDeploying CCIPWETH9Bridge...");
            deployed.ccipWETH9Bridge = deployCCIPWETH9Bridge(
                ccipConfig.router,
                deployed.weth9,
                ccipConfig.linkToken
            );

            console.log("\nDeploying CCIPWETH10Bridge...");
            deployed.ccipWETH10Bridge = deployCCIPWETH10Bridge(
                ccipConfig.router,
                deployed.weth10,
                ccipConfig.linkToken
            );

            // Deploy CCIPLogger
            console.log("\nDeploying CCIPLogger...");
            deployed.ccipLogger = deployCcipLogger(ccipConfig);
        }

        vm.stopBroadcast();

        // Print deployment summary
        printDeploymentSummary(chainId);
    }

    /**
     * @notice Get CCIP configuration for a specific chain
     */
    function getCCIPConfig(uint256 chainId) internal view returns (CCIPConfig memory) {
        if (chainId == MAINNET) {
            return CCIPConfig({
                router: vm.envAddress("CCIP_ETH_ROUTER"),
                linkToken: vm.envAddress("CCIP_ETH_LINK_TOKEN"),
                chainSelector: uint64(vm.envUint("ETH_MAINNET_SELECTOR"))
            });
        } else if (chainId == CRONOS) {
            return CCIPConfig({
                router: vm.envAddress("CCIP_CRONOS_ROUTER"),
                linkToken: vm.envAddress("CCIP_CRONOS_LINK_TOKEN"),
                chainSelector: uint64(vm.envUint("CRONOS_SELECTOR"))
            });
        } else if (chainId == BSC) {
            return CCIPConfig({
                router: vm.envAddress("CCIP_BSC_ROUTER"),
                linkToken: vm.envAddress("CCIP_BSC_LINK_TOKEN"),
                chainSelector: uint64(vm.envUint("BSC_SELECTOR"))
            });
        } else if (chainId == POLYGON) {
            return CCIPConfig({
                router: vm.envAddress("CCIP_POLYGON_ROUTER"),
                linkToken: vm.envAddress("CCIP_POLYGON_LINK_TOKEN"),
                chainSelector: uint64(vm.envUint("POLYGON_SELECTOR"))
            });
        } else if (chainId == GNOSIS) {
            return CCIPConfig({
                router: vm.envAddress("CCIP_GNOSIS_ROUTER"),
                linkToken: vm.envAddress("CCIP_GNOSIS_LINK_TOKEN"),
                chainSelector: uint64(vm.envUint("GNOSIS_SELECTOR"))
            });
        } else if (chainId == AVALANCHE) {
            return CCIPConfig({
                router: vm.envAddress("CCIP_AVALANCHE_ROUTER"),
                linkToken: vm.envAddress("CCIP_AVALANCHE_LINK_TOKEN"),
                chainSelector: uint64(vm.envUint("AVALANCHE_SELECTOR"))
            });
        } else if (chainId == BASE) {
            return CCIPConfig({
                router: vm.envAddress("CCIP_BASE_ROUTER"),
                linkToken: vm.envAddress("CCIP_BASE_LINK_TOKEN"),
                chainSelector: uint64(vm.envUint("BASE_SELECTOR"))
            });
        } else if (chainId == ARBITRUM) {
            return CCIPConfig({
                router: vm.envAddress("CCIP_ARBITRUM_ROUTER"),
                linkToken: vm.envAddress("CCIP_ARBITRUM_LINK_TOKEN"),
                chainSelector: uint64(vm.envUint("ARBITRUM_SELECTOR"))
            });
        } else if (chainId == OPTIMISM) {
            return CCIPConfig({
                router: vm.envAddress("CCIP_OPTIMISM_ROUTER"),
                linkToken: vm.envAddress("CCIP_OPTIMISM_LINK_TOKEN"),
                chainSelector: uint64(vm.envUint("OPTIMISM_SELECTOR"))
            });
        } else if (chainId == MONAD) {
            return CCIPConfig({
                router: vm.envOr("CCIP_MONAD_ROUTER", CCIP_ROUTER_MONAD_DEFAULT),
                linkToken: vm.envOr("CCIP_MONAD_LINK_TOKEN", address(0)),
                chainSelector: uint64(vm.envOr("MONAD_SELECTOR", uint256(MONAD_SELECTOR_DEFAULT)))
            });
        } else {
            revert("Unsupported chain");
        }
    }

    /**
     * @notice Get WETH configuration for a specific chain
     */
    function getWETHConfig(uint256 chainId) internal returns (WETHConfig memory) {
        if (chainId == MAINNET) {
            return WETHConfig({
                weth9: vm.envAddress("WETH9_MAINNET"),
                weth10: vm.envAddress("WETH10_MAINNET")
            });
        } else if (chainId == CRONOS) {
            address weth9 = vm.envOr("WETH9_CRONOS", address(0));
            address weth10 = vm.envOr("WETH10_CRONOS", address(0));
            return WETHConfig({weth9: weth9, weth10: weth10});
        } else if (chainId == BSC) {
            address weth9 = vm.envOr("WETH9_BSC", address(0));
            address weth10 = vm.envOr("WETH10_BSC", address(0));
            return WETHConfig({weth9: weth9, weth10: weth10});
        } else if (chainId == POLYGON) {
            address weth9 = vm.envOr("WETH9_POLYGON", address(0));
            address weth10 = vm.envOr("WETH10_POLYGON", address(0));
            return WETHConfig({weth9: weth9, weth10: weth10});
        } else if (chainId == GNOSIS) {
            address weth9 = vm.envOr("WETH9_GNOSIS", address(0));
            address weth10 = vm.envOr("WETH10_GNOSIS", address(0));
            return WETHConfig({weth9: weth9, weth10: weth10});
        } else if (chainId == AVALANCHE) {
            address weth9 = vm.envOr("WETH9_AVALANCHE", address(0));
            address weth10 = vm.envOr("WETH10_AVALANCHE", address(0));
            return WETHConfig({weth9: weth9, weth10: weth10});
        } else if (chainId == BASE) {
            address weth9 = vm.envOr("WETH9_BASE", address(0));
            address weth10 = vm.envOr("WETH10_BASE", address(0));
            return WETHConfig({weth9: weth9, weth10: weth10});
        } else if (chainId == ARBITRUM) {
            address weth9 = vm.envOr("WETH9_ARBITRUM", address(0));
            address weth10 = vm.envOr("WETH10_ARBITRUM", address(0));
            return WETHConfig({weth9: weth9, weth10: weth10});
        } else if (chainId == OPTIMISM) {
            address weth9 = vm.envOr("WETH9_OPTIMISM", address(0));
            address weth10 = vm.envOr("WETH10_OPTIMISM", address(0));
            return WETHConfig({weth9: weth9, weth10: weth10});
        } else if (chainId == MONAD) {
            address weth9 = vm.envOr("WETH9_MONAD", address(0));
            address weth10 = vm.envOr("WETH10_MONAD", address(0));
            if (weth9 == address(0)) {
                weth9 = WMON_MAINNET;
            }
            if (weth10 == address(0)) {
                weth10 = WMON_MAINNET;
            }
            return WETHConfig({weth9: weth9, weth10: weth10});
        } else {
            revert("Unsupported chain");
        }
    }

    /**
     * @notice Deploy WETH9 contract
     */
    function deployWETH9() internal returns (address) {
        WETH weth9 = new WETH();
        return address(weth9);
    }

    /**
     * @notice Deploy WETH10 contract
     */
    function deployWETH10() internal returns (address) {
        WETH10 weth10 = new WETH10();
        return address(weth10);
    }

    /**
     * @notice Deploy CCIPWETH9Bridge
     */
    function deployCCIPWETH9Bridge(
        address router,
        address weth9,
        address feeToken
    ) internal returns (address) {
        CCIPWETH9Bridge bridge = new CCIPWETH9Bridge(router, weth9, feeToken);
        return address(bridge);
    }

    /**
     * @notice Deploy CCIPWETH10Bridge
     */
    function deployCCIPWETH10Bridge(
        address router,
        address weth10,
        address feeToken
    ) internal returns (address) {
        CCIPWETH10Bridge bridge = new CCIPWETH10Bridge(router, weth10, feeToken);
        return address(bridge);
    }

    /**
     * @notice Deploy CCIPLogger
     * @dev Note: This assumes CCIPLogger contract exists. If it uses Hardhat/OpenZeppelin,
     *      you may need to deploy it separately using the Hardhat script.
     */
    function deployCcipLogger(CCIPConfig memory config) internal returns (address) {
        // TODO: If CCIPLogger is a Solidity contract in this repo, deploy it here
        // For now, this is a placeholder. CCIPLogger may need to be deployed via Hardhat
        // due to OpenZeppelin dependencies.

        // Example deployment (adjust based on actual CCIPLogger contract):
        // CCIPLogger logger = new CCIPLogger(
        //     config.router,
        //     vm.envOr("AUTHORIZED_SIGNER", address(0)),
        //     config.chainSelector
        // );
        // return address(logger);

        console.log("WARNING: CCIPLogger deployment not implemented in Foundry script.");
        console.log("Please deploy CCIPLogger using the Hardhat script:");
        console.log("  npm run deploy:logger:mainnet (for mainnet)");
        console.log("  Or create a chain-specific deployment script.");

        return address(0); // Placeholder
    }

    /**
     * @notice Print deployment summary
     */
    function printDeploymentSummary(uint256 chainId) internal view {
        console.log("\n==========================================");
        console.log("Deployment Summary - Chain ID:", chainId);
        console.log("==========================================");

        if (chainId != MAINNET) {
            console.log("WETH9:", deployed.weth9);
            console.log("WETH10:", deployed.weth10);
            console.log("CCIPWETH9Bridge:", deployed.ccipWETH9Bridge);
            console.log("CCIPWETH10Bridge:", deployed.ccipWETH10Bridge);
        }

        console.log("CCIPLogger:", deployed.ccipLogger);

        console.log("\n==========================================");
        console.log("Next Steps:");
        console.log("1. Verify contracts on explorer");
        console.log("2. Update .env with deployed addresses");
        console.log("3. Configure bridge destinations");
        console.log("4. Test cross-chain transfers");
        console.log("==========================================");
    }
}

