contract FIRETHEBOSS is ERC721Enumerable, Ownable {
    using Strings for uint256;

    bool public _isSaleActive = false;

    // Constants
    uint256 public constant MAX_SUPPLY = 1039;
    uint256 public constant TEAM_RESERVE = 9;
    uint256 public earlyPassBalance = 122;
    uint256 public eventBalance = 20;
    uint256 public publicMintBalance = 888;
    uint256 public mintPrice = 0.25 ether;
    uint256 public roundBalance = 0;
    uint256 public maxMint = 5;

    string baseURI;
    string public baseExtension = ".json";

    mapping(uint256 => string) private _tokenURIs;

    constructor(string memory initBaseURI)
        ERC721("FIRE THE BOSS", "FTB")
    {
        setBaseURI(initBaseURI);
        // Team Reservation
        for (uint256 i = 0; i < TEAM_RESERVE; i++) {
            _safeMint(0x9Cc5d37D2053dBa7e6255068d127C1Eaaad2199F, i);
        }
    }

    function mintFTB(uint256 mintBalance) public payable {
        require(msg.sender == tx.origin, "Should not mint through contracts");
        require(_isSaleActive, "Sale is not active now");
        require(totalSupply() + mintBalance <= MAX_SUPPLY, "exceed max supply");
        require(mintBalance <= roundBalance, "exceed round balance");
        require(mintBalance <= publicMintBalance, "No enough public mint quata");
        require(mintBalance <= maxMint, "exceed max mint per time");
        require(mintBalance * mintPrice <= msg.value, "Not enough ether");

        roundBalance -= mintBalance;
        publicMintBalance -= mintBalance;
        _mintFTB(msg.sender, mintBalance);
    }

    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");

        string memory _tokenURI = _tokenURIs[tokenId];
        string memory base = _baseURI();

        // If there is no base URI, return the token URI.
        if (bytes(base).length == 0) {
            return _tokenURI;
        }
        // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
        if (bytes(_tokenURI).length > 0) {
            return string(abi.encodePacked(base, _tokenURI));
        }
        // If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI.
        return string(abi.encodePacked(base, tokenId.toString(), baseExtension));
    }

    // internal Functions
    function _baseURI() internal view virtual override returns (string memory) {
        return baseURI;
    }

    function _mintFTB(address to, uint256 mintBalance) internal {
        for (uint256 i = 0; i < mintBalance; i++) {
            uint256 mintIndex = totalSupply();
            if (totalSupply() < MAX_SUPPLY) {
                _safeMint(to, mintIndex);
            }
        }
    }

    // onlyOwner Functions

    // Airdrop for Influencers and Events, limited 1 per time
    function airdropEvent(address to) public onlyOwner {
        require(totalSupply() + 1 <= MAX_SUPPLY, "Airdrop would exceed max supply");
        require(eventBalance - 1 >= 0, "No more airdrop quota");

        eventBalance -= 1;
        _mintFTB(to, 1);
    }

    // Airdrop for Early Pass and Dessert Shop
    function airdropEarlyPass(address to, uint256 airdropBalance) public onlyOwner {
        require(totalSupply() + airdropBalance <= MAX_SUPPLY, "Airdrop would exceed max supply");
        require(earlyPassBalance - airdropBalance >= 0, "No more airdrop quota");

        earlyPassBalance -= airdropBalance;
        _mintFTB(to, airdropBalance);
    }

    function flipSaleState() public onlyOwner {
        _isSaleActive = !_isSaleActive;
    }

    function setRoundBalance(uint256 _roundBalance) public onlyOwner {
        roundBalance = _roundBalance;
    }

    function setMintPrice(uint256 _mintPrice) public onlyOwner {
        mintPrice = _mintPrice;
    }

    function setMaxMint(uint256 _maxMint) public onlyOwner {
        maxMint = _maxMint;
    }

    function setBaseURI(string memory _newBaseURI) public onlyOwner {
        baseURI = _newBaseURI;
    }

    function setBaseExtension(string memory _newBaseExtension) public onlyOwner {
        baseExtension = _newBaseExtension;
    }

    function withdraw(address to) public onlyOwner {
        uint256 balance = address(this).balance;
        payable(to).transfer(balance);
    }
}
function airdropEarlyPass(address to, uint256 airdropBalance) public onlyOwner {
    require(totalSupply() + airdropBalance <= MAX_SUPPLY, "Airdrop would exceed max supply");
    require(earlyPassBalance - airdropBalance >= 0, "No more airdrop quota");

    earlyPassBalance -= airdropBalance;
    _mintFTB(to, airdropBalance);
}

// Airdrop for Early Pass and Dessert Shop