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

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ProtectedContract, ContractType} from "./HypernativeModuleStructsAndEnums.sol";

interface ISafe {
    function execTransactionFromModule(address to, uint256 value, bytes memory data, uint8 operation) external returns (bool success);
}

contract HypernativeModule is Ownable {
    address public updater;
    bytes constant public pauseSelector = abi.encodeWithSignature("pause()");
    bytes constant public emergencyShutdownSelector = abi.encodeWithSignature("emergencyShutdown()");
    
    mapping (ContractType => bytes) public contractTypeToPauseFunction;
    ProtectedContract[] public protectedContracts;
    ISafe public safe;

    constructor(address multisig, address _updater, ProtectedContract[] memory _protectedContracts) {
        safe = ISafe(multisig);
        updater = _updater;
        contractTypeToPauseFunction[ContractType.JUNIOR_OR_SENIOR] = pauseSelector;
        contractTypeToPauseFunction[ContractType.TRANCH] = emergencyShutdownSelector;
        for (uint i = 0; i < _protectedContracts.length; ++i) {
            protectedContracts.push(_protectedContracts[i]);
        }
    }

    modifier onlyUpdater() {
        require(msg.sender == updater, "HypernativeModule: Only Updater");
        _;
    }

    modifier onlyOwnerOrUpdater() {
        require(msg.sender == updater || msg.sender == owner(), "HypernativeModule: Only Updater or Owner");
        _;
    }

    function pauseAllContracts() public onlyOwner {
        uint256 protectedContractLength = protectedContracts.length;
        for (uint i = 0; i < protectedContractLength;) {
            safe.execTransactionFromModule(protectedContracts[i].contractAddress, 0, contractTypeToPauseFunction[protectedContracts[i].contractType],0);
            unchecked {
                ++i;
            }
        }
    }

    function pauseContractsByType(ContractType _contractType) public onlyOwner {
        uint256 protectedContractLength = protectedContracts.length;
        for (uint i = 0; i < protectedContractLength;) {
            if (protectedContracts[i].contractType == _contractType) {
                safe.execTransactionFromModule(protectedContracts[i].contractAddress, 0, contractTypeToPauseFunction[_contractType], 0);
            }
            unchecked {
                ++i;
            }
        }
    }

    function updateProtectedContracts(ProtectedContract[] memory _protectedContracts) public onlyUpdater {
        uint256 _protectedContractsLength = _protectedContracts.length;
        for (uint i = 0; i < _protectedContractsLength;) {
            protectedContracts.push(_protectedContracts[i]);
            unchecked {
                ++i;
            }
        }

    }
    
    function replaceProtectedContracts(ProtectedContract[] memory _protectedContracts) public onlyUpdater {
        assembly {
            sstore(protectedContracts.slot, mload(_protectedContracts))
        }
        uint256 _protectedContractsLength = _protectedContracts.length;
        for (uint i = 0; i < _protectedContractsLength;) {
            protectedContracts[i] = _protectedContracts[i];
            unchecked {
                ++i;
            }
        }
    }

    function updateMultisigAddress(address newMultisig) public onlyOwner {
        safe = ISafe(newMultisig);
    }

    function getProctectedContracts() public view returns (ProtectedContract[] memory) {
        return protectedContracts;
    }

    function isContractProtected(address _address) public view returns (bool) {
        for (uint i = 0; i < protectedContracts.length; ++i) {
            if (protectedContracts[i].contractAddress == _address) {
                return true;
            }
        }
        return false;
    }

    function changeUpdater(address _updater) public onlyOwnerOrUpdater {
        updater = _updater;
    }
}
[{"inputs":[{"internalType":"address","name":"multisig","type":"address"},{"internalType":"address","name":"_updater","type":"address"},{"components":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"enum ContractType","name":"contractType","type":"uint8"}],"internalType":"struct ProtectedContract[]","name":"_protectedContracts","type":"tuple[]"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"address","name":"_updater","type":"address"}],"name":"changeUpdater","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum ContractType","name":"","type":"uint8"}],"name":"contractTypeToPauseFunction","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"emergencyShutdownSelector","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getProctectedContracts","outputs":[{"components":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"enum ContractType","name":"contractType","type":"uint8"}],"internalType":"struct ProtectedContract[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"isContractProtected","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pauseAllContracts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum ContractType","name":"_contractType","type":"uint8"}],"name":"pauseContractsByType","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pauseSelector","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"protectedContracts","outputs":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"enum ContractType","name":"contractType","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"enum ContractType","name":"contractType","type":"uint8"}],"internalType":"struct ProtectedContract[]","name":"_protectedContracts","type":"tuple[]"}],"name":"replaceProtectedContracts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"safe","outputs":[{"internalType":"contract ISafe","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newMultisig","type":"address"}],"name":"updateMultisigAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"enum ContractType","name":"contractType","type":"uint8"}],"internalType":"struct ProtectedContract[]","name":"_protectedContracts","type":"tuple[]"}],"name":"updateProtectedContracts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updater","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]