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

import "forge-std/Script.sol";
import {CREATE2Factory} from "../../contracts/utils/CREATE2Factory.sol";
import {MirrorRegistry} from "../../contracts/mirror/MirrorRegistry.sol";
import {UniversalCCIPBridge} from "../../contracts/bridge/UniversalCCIPBridge.sol";
import {UniversalAssetRegistry} from "../../contracts/registry/UniversalAssetRegistry.sol";
import {AlltraAdapter} from "../../contracts/bridge/adapters/evm/AlltraAdapter.sol";
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";

/**
 * @title DeployDeterministicCore
 * @notice Deploy MirrorRegistry, UniversalCCIPBridge (proxy), UniversalAssetRegistry, AlltraAdapter via CREATE2 with fixed salts.
 * @dev Use identical ADMIN and (where applicable) ASSET_REGISTRY per chain so addresses match. Call setCCIPRouter(router) on bridge after deploy on each chain.
 * See docs/runbooks/MULTI_CHAIN_EXECUTION_DETERMINISTIC_DEPLOYMENT.md
 * Refactored to avoid Yul stack-too-deep: single struct + internal deployAll to minimize run() stack.
 */
contract DeployDeterministicCore is Script {
    uint256 constant SALT_MIRROR_REGISTRY = uint256(keccak256("MirrorRegistry"));
    uint256 constant SALT_UNIVERSAL_ASSET_REGISTRY_IMPL = uint256(keccak256("UniversalAssetRegistry"));
    uint256 constant SALT_UNIVERSAL_ASSET_REGISTRY_PROXY = uint256(keccak256("UniversalAssetRegistry.Proxy"));
    uint256 constant SALT_CCIP_BRIDGE_IMPL = uint256(keccak256("UniversalCCIPBridge"));
    uint256 constant SALT_CCIP_BRIDGE_PROXY = uint256(keccak256("UniversalCCIPBridge.Proxy"));
    uint256 constant SALT_ALLTRA_ADAPTER = uint256(keccak256("AlltraAdapter"));

    struct DeployResult {
        address factory;
        address registry;
        address bridgeProxy;
        address mirror;
        address adapter;
    }

    function run() external {
        uint256 pk = vm.envUint("PRIVATE_KEY");
        address admin = vm.envOr("ADMIN", vm.addr(pk));
        vm.startBroadcast(pk);
        DeployResult memory r = _deployAll(admin);
        vm.stopBroadcast();
        console.log("CREATE2Factory:", r.factory);
        console.log("UniversalAssetRegistry:", r.registry);
        console.log("UniversalCCIPBridge proxy:", r.bridgeProxy);
        console.log("MirrorRegistry:", r.mirror);
        console.log("AlltraAdapter:", r.adapter);
        console.log("\n--- Deterministic deployment summary ---");
        console.log("Call UniversalCCIPBridge.setCCIPRouter(router) on each chain after deploy.");
    }

    function _deployAll(address admin) internal returns (DeployResult memory r) {
        CREATE2Factory factory = new CREATE2Factory();
        r.factory = address(factory);

        address registryImpl = factory.deploy(type(UniversalAssetRegistry).creationCode, SALT_UNIVERSAL_ASSET_REGISTRY_IMPL);
        bytes memory registryInitData = abi.encodeCall(UniversalAssetRegistry.initialize, (admin));
        bytes memory registryProxyBytecode = abi.encodePacked(type(ERC1967Proxy).creationCode, abi.encode(registryImpl, registryInitData));
        r.registry = factory.deploy(registryProxyBytecode, SALT_UNIVERSAL_ASSET_REGISTRY_PROXY);

        address impl = factory.deploy(type(UniversalCCIPBridge).creationCode, SALT_CCIP_BRIDGE_IMPL);
        address ccipRouter = vm.envOr("CCIP_ROUTER", address(0));
        bytes memory initData = abi.encodeCall(UniversalCCIPBridge.initialize, (r.registry, ccipRouter, admin));
        bytes memory proxyBytecode = abi.encodePacked(type(ERC1967Proxy).creationCode, abi.encode(impl, initData));
        r.bridgeProxy = factory.deploy(proxyBytecode, SALT_CCIP_BRIDGE_PROXY);

        r.mirror = factory.deploy(
            abi.encodePacked(type(MirrorRegistry).creationCode, abi.encode(admin)),
            SALT_MIRROR_REGISTRY
        );
        r.adapter = factory.deploy(
            abi.encodePacked(type(AlltraAdapter).creationCode, abi.encode(admin, r.bridgeProxy)),
            SALT_ALLTRA_ADAPTER
        );
    }
}
