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

import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "../vendor/openzeppelin/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "../registry/UniversalAssetRegistry.sol";
import "./UniversalCCIPBridge.sol";

/**
 * @title BridgeOrchestrator
 * @notice Routes bridge requests to appropriate asset-specific bridges
 * @dev Central routing layer for multi-asset bridge system
 */
contract BridgeOrchestrator is 
    Initializable,
    AccessControlUpgradeable,
    UUPSUpgradeable
{
    bytes32 public constant ROUTER_ADMIN_ROLE = keccak256("ROUTER_ADMIN_ROLE");
    bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE");

    // Core dependencies
    UniversalAssetRegistry public assetRegistry;
    UniversalCCIPBridge public defaultBridge;
    
    // Asset type to bridge mapping
    mapping(bytes32 => address) public assetTypeToBridge;
    mapping(address => bool) public isRegisteredBridge;
    
    // Routing statistics
    struct RoutingStats {
        uint256 totalBridges;
        uint256 successfulBridges;
        uint256 failedBridges;
        uint256 lastBridgeTime;
    }
    
    mapping(address => RoutingStats) public bridgeStats;
    mapping(UniversalAssetRegistry.AssetType => RoutingStats) public assetTypeStats;

    event BridgeRouted(
        address indexed token,
        UniversalAssetRegistry.AssetType assetType,
        address indexed bridgeContract,
        bytes32 indexed messageId
    );
    
    event AssetTypeBridgeRegistered(
        bytes32 indexed assetTypeHash,
        UniversalAssetRegistry.AssetType assetType,
        address bridgeContract
    );
    
    event BridgeUnregistered(
        bytes32 indexed assetTypeHash,
        address bridgeContract
    );

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    function initialize(
        address _assetRegistry,
        address _defaultBridge,
        address admin
    ) external initializer {
        __AccessControl_init();
        __UUPSUpgradeable_init();
        
        require(_assetRegistry != address(0), "Zero registry");
        require(_defaultBridge != address(0), "Zero bridge");
        
        assetRegistry = UniversalAssetRegistry(_assetRegistry);
        defaultBridge = UniversalCCIPBridge(payable(_defaultBridge));
        
        _grantRole(DEFAULT_ADMIN_ROLE, admin);
        _grantRole(ROUTER_ADMIN_ROLE, admin);
        _grantRole(UPGRADER_ROLE, admin);
    }

    function _authorizeUpgrade(address newImplementation)
        internal override onlyRole(UPGRADER_ROLE) {}

    /**
     * @notice Route bridge request to appropriate bridge
     */
    function bridge(
        UniversalCCIPBridge.BridgeOperation calldata op
    ) external payable returns (bytes32 messageId) {
        // Get asset information
        UniversalAssetRegistry.UniversalAsset memory asset = assetRegistry.getAsset(op.token);
        require(asset.isActive, "Asset not active");
        
        // Get bridge contract for this asset type
        bytes32 assetTypeHash = bytes32(uint256(asset.assetType));
        address bridgeContract = assetTypeToBridge[assetTypeHash];
        
        // Use default bridge if no specialized bridge
        if (bridgeContract == address(0)) {
            bridgeContract = address(defaultBridge);
        }
        
        require(isRegisteredBridge[bridgeContract], "Bridge not registered");
        
        // Forward call to specialized bridge
        (bool success, bytes memory data) = bridgeContract.call{value: msg.value}(
            abi.encodeWithSelector(
                UniversalCCIPBridge.bridge.selector,
                op
            )
        );
        
        require(success, "Bridge call failed");
        messageId = abi.decode(data, (bytes32));
        
        // Update statistics
        _updateStats(bridgeContract, asset.assetType, true);
        
        emit BridgeRouted(op.token, asset.assetType, bridgeContract, messageId);
        
        return messageId;
    }

    /**
     * @notice Register asset type bridge
     */
    function registerAssetTypeBridge(
        UniversalAssetRegistry.AssetType assetType,
        address bridgeContract
    ) external onlyRole(ROUTER_ADMIN_ROLE) {
        require(bridgeContract != address(0), "Zero address");
        require(bridgeContract.code.length > 0, "Not a contract");
        
        bytes32 assetTypeHash = bytes32(uint256(assetType));
        assetTypeToBridge[assetTypeHash] = bridgeContract;
        isRegisteredBridge[bridgeContract] = true;
        
        emit AssetTypeBridgeRegistered(assetTypeHash, assetType, bridgeContract);
    }

    /**
     * @notice Unregister bridge
     */
    function unregisterBridge(
        UniversalAssetRegistry.AssetType assetType
    ) external onlyRole(ROUTER_ADMIN_ROLE) {
        bytes32 assetTypeHash = bytes32(uint256(assetType));
        address bridgeContract = assetTypeToBridge[assetTypeHash];
        
        delete assetTypeToBridge[assetTypeHash];
        // Note: We don't remove from isRegisteredBridge in case it's used elsewhere
        
        emit BridgeUnregistered(assetTypeHash, bridgeContract);
    }

    /**
     * @notice Update routing statistics
     */
    function _updateStats(
        address bridgeContract,
        UniversalAssetRegistry.AssetType assetType,
        bool success
    ) internal {
        RoutingStats storage bridgeStat = bridgeStats[bridgeContract];
        RoutingStats storage typeStat = assetTypeStats[assetType];
        
        bridgeStat.totalBridges++;
        typeStat.totalBridges++;
        
        if (success) {
            bridgeStat.successfulBridges++;
            typeStat.successfulBridges++;
        } else {
            bridgeStat.failedBridges++;
            typeStat.failedBridges++;
        }
        
        bridgeStat.lastBridgeTime = block.timestamp;
        typeStat.lastBridgeTime = block.timestamp;
    }

    /**
     * @notice Set default bridge
     */
    function setDefaultBridge(address _defaultBridge) external onlyRole(DEFAULT_ADMIN_ROLE) {
        require(_defaultBridge != address(0), "Zero address");
        defaultBridge = UniversalCCIPBridge(payable(_defaultBridge));
        isRegisteredBridge[_defaultBridge] = true;
    }

    // View functions
    
    function getBridgeForAssetType(UniversalAssetRegistry.AssetType assetType)
        external view returns (address) {
        bytes32 assetTypeHash = bytes32(uint256(assetType));
        address bridgeAddr = assetTypeToBridge[assetTypeHash];
        return bridgeAddr != address(0) ? bridgeAddr : address(defaultBridge);
    }

    function getBridgeStats(address bridgeContract) 
        external view returns (RoutingStats memory) {
        return bridgeStats[bridgeContract];
    }

    function getAssetTypeStats(UniversalAssetRegistry.AssetType assetType)
        external view returns (RoutingStats memory) {
        return assetTypeStats[assetType];
    }
}
