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

contract BulkRegister {
    ENSController ensRegistrarController =
        ENSController(0x283Af0B28c62C092C9727F1Ee09c02CA627EB7F5);

    function getOwner() private pure returns (address) {
        return 0xb931592eEd3f82681E00bfd05ceD4C11bE957115;
    }

    modifier onlyOwner() {
        require(msg.sender == getOwner(), "Not owner");
        _;
    }

    // use external because cheaper and we never want to call it privately.
    function withdraw() external {
        (bool success, ) = getOwner().call{value: address(this).balance}("");
        require(success, "Failed to send Ether");
    }

    function validPrice(
        string[] memory names,
        uint256 duration,
        uint256 ethSent
    ) internal view returns (uint256[] memory) {
        uint256[] memory prices = new uint256[](names.length);
        uint256 totalPrice;
        for (uint256 i = 0; i < names.length; i++) {
            prices[i] = ensRegistrarController.rentPrice(names[i], duration);
            totalPrice += prices[i];
        }
        require(ethSent >= totalPrice, "Invalid price");
        return prices;
    }

    function createCommitmentsForRegistration(
        ENSCommitment.Commitment[] memory commitments,
        uint256 duration,
        bool withConfigs
    ) external view returns (bytes32[] memory, uint256[] memory) {
        bytes32[] memory createdCommitments = new bytes32[](commitments.length);
        if (withConfigs == false) {
            for (uint8 i = 0; i < commitments.length; i++) {
                createdCommitments[i] = ensRegistrarController.makeCommitment(
                    commitments[i].name,
                    commitments[i].owner,
                    commitments[i].secret
                );
            }
        } else {
            for (uint8 i = 0; i < commitments.length; i++) {
                createdCommitments[i] = ensRegistrarController
                    .makeCommitmentWithConfig(
                        commitments[i].name,
                        commitments[i].owner,
                        commitments[i].secret,
                        commitments[i].resolver,
                        commitments[i].owner
                    );
            }
        }

        uint256[] memory prices = new uint256[](commitments.length);
        for (uint8 i = 0; i < commitments.length; i++) {
            prices[i] = ensRegistrarController.rentPrice(
                commitments[i].name,
                duration
            );
        }

        return (createdCommitments, prices);
    }

    function requestRegistration(bytes32[] memory commitments) external {
        for (uint8 i = 0; i < commitments.length; i++) {
            ensRegistrarController.commit(commitments[i]);
        }
    }

    function completeRegistration(
        string[] memory names,
        address owner,
        uint256 duration,
        bytes32 secret
    ) external payable {
        uint256[] memory prices = validPrice(names, duration, msg.value);

        for (uint8 i = 0; i < names.length; i++) {
            ensRegistrarController.register{value: prices[i]}(
                names[i],
                owner,
                duration,
                secret
            );
        }
    }

    function completeRegistrationWithConfigs(
        string[] memory names,
        address owner,
        uint256 duration,
        bytes32 secret,
        address resolver
    ) external payable {
        uint256[] memory prices = validPrice(names, duration, msg.value);

        for (uint8 i = 0; i < names.length; i++) {
            ensRegistrarController.registerWithConfig{value: prices[i]}(
                names[i],
                owner,
                duration,
                secret,
                resolver,
                owner
            );
        }
    }
}

contract ENSCommitment {
    struct Commitment {
        string name;
        address owner;
        uint256 duration;
        bytes32 secret;
        address resolver;
        bytes[] data;
        bool reverseRecord;
        uint32 fuses;
        uint64 wrapperExpiry;
    }
    struct RegistrationWithConfig {
        string name;
        address owner;
    }
}

interface ENSController {
    event NameRegistered(
        string name,
        bytes32 indexed label,
        address indexed owner,
        uint256 cost,
        uint256 expires
    );
    event NameRenewed(
        string name,
        bytes32 indexed label,
        uint256 cost,
        uint256 expires
    );
    event NewPriceOracle(address indexed oracle);

    function rentPrice(
        string memory name,
        uint256 duration
    ) external view returns (uint256);

    function valid(string memory name) external pure returns (bool);

    function available(string memory name) external view returns (bool);

    function makeCommitment(
        string memory name,
        address owner,
        bytes32 secret
    ) external pure returns (bytes32);

    function makeCommitmentWithConfig(
        string memory name,
        address owner,
        bytes32 secret,
        address resolver,
        address addr
    ) external pure returns (bytes32);

    function commit(bytes32 commitment) external;

    function register(
        string calldata name,
        address owner,
        uint256 duration,
        bytes32 secret
    ) external payable;

    function registerWithConfig(
        string memory name,
        address owner,
        uint256 duration,
        bytes32 secret,
        address resolver,
        address addr
    ) external payable;

    function renew(string calldata name, uint256 duration) external payable;
}
[{"inputs":[{"internalType":"string[]","name":"names","type":"string[]"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"bytes32","name":"secret","type":"bytes32"}],"name":"completeRegistration","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"string[]","name":"names","type":"string[]"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"bytes32","name":"secret","type":"bytes32"},{"internalType":"address","name":"resolver","type":"address"}],"name":"completeRegistrationWithConfigs","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"bytes32","name":"secret","type":"bytes32"},{"internalType":"address","name":"resolver","type":"address"},{"internalType":"bytes[]","name":"data","type":"bytes[]"},{"internalType":"bool","name":"reverseRecord","type":"bool"},{"internalType":"uint32","name":"fuses","type":"uint32"},{"internalType":"uint64","name":"wrapperExpiry","type":"uint64"}],"internalType":"struct ENSCommitment.Commitment[]","name":"commitments","type":"tuple[]"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"bool","name":"withConfigs","type":"bool"}],"name":"createCommitmentsForRegistration","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"commitments","type":"bytes32[]"}],"name":"requestRegistration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]