// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

interface Token {
    function transfer(address to, uint256 value) external returns (bool);
}

contract TokenDistribution {
    mapping(address => uint256) public claimableAmounts;
    address public tokenContractAddress;
    address public owner;

    event TokensClaimed(address indexed user, uint256 amount);

    constructor(address _tokenContractAddress) {
        tokenContractAddress = _tokenContractAddress;
        owner = msg.sender;
        claimableAmounts[0x50D889511FCEC4c5cfc564aD4E32e1627964A937] = 20000;
        claimableAmounts[0x8d859AAE6106af7d7eE1B503006608cc355E40de] = 19998;
        claimableAmounts[0xbc25ABeeeEdf05646175A07eA45Cd3c1bb56C9F1] = 19918;
        claimableAmounts[0xee7bE1fCA01232aE37D7A23Ee1d6FDfB7EfA9E5d] = 19800;
        claimableAmounts[0xedda8A78712100c2562915E5DB67853A73434536] = 19751;
        claimableAmounts[0xF04af1274471A6C6eb5F89DEe50909fe65C99C84] = 19733;
        claimableAmounts[0x5bD85C7E0A0f562B6c331B1bb6a507b51072019f] = 19486;
        claimableAmounts[0x03F3ec0d1ca375C722aD92BD6104f7cf458D64BA] = 19012;
        claimableAmounts[0x68d46D82d705E18145880321e46794bD451D3A95] = 19000;
        claimableAmounts[0x263cAF306Fdb3B24a2776818a7B0cfDB5cC03fD9] = 18900;
        claimableAmounts[0x62821595AC6d55AF0cebc843e5Bc0C8bB4d08778] = 18600;
        claimableAmounts[0x981D088D046f30F46EF2f11FE99ee1E0bCF359D4] = 18246;
        claimableAmounts[0x1828486d9c4F4424535Ef75E490C559a9C3f87a5] = 18100;
        claimableAmounts[0x6F56Ad5e71e15535C18A3a9Ab6fF83084eaDC67e] = 17815;
        claimableAmounts[0xDC341a2AF8F206B5D2EbECD7d7DF0E0646A2A822] = 17223;
        claimableAmounts[0x656863696deEf9bf3ec6Ad7b6136F7ab1e5ef433] = 17090;
        claimableAmounts[0xff18AC2a5d2c003602316CeC430a50b72CFDa3e4] = 16792;
        claimableAmounts[0x85900C4218A4650ac66A5cf2296E8Da104f5DE5A] = 16782;
        claimableAmounts[0xb5fb0f3DC8Eed6279DF3c093f7C7Ab1E299A760c] = 16750;
        claimableAmounts[0x2d7D259b7Dc08ABB4A28703F7cF5a536C03D6219] = 16340;
        claimableAmounts[0x2880e17965AaE6C7e7EEE2fcC1093670D2a0A855] = 15860;
        claimableAmounts[0xFe3b3aBd9a1463A59101e6332fC4A795A207E3ae] = 14570;
        claimableAmounts[0xEA7eEA3735592Df7D6822d1c2f1A314eDA66ca8e] = 13723;
        claimableAmounts[0xb8f9e33fE0a4a21E6e0F70d88d6904Fe8137A7FE] = 9900;
        claimableAmounts[0x9C170ed7d5b8f841e51447d4D107c75D104fFCD9] = 8734;
        claimableAmounts[0x3Bd9675780051A7315a327Edd1FECD86B165700D] = 4782;
        claimableAmounts[0xea631A62EEb6BBfcD61fA6E2b9c588d882e98539] = 4292;
        claimableAmounts[0xcE7ce96e5e11a8Ef00046f7805bC673c70A6948E] = 3485;
        claimableAmounts[0xC3B1Df34f6e864878eFc4A884A100bC68c485302] = 3481;
        claimableAmounts[0xC63A7794b034471c7ea446d33bF344aB76F7c833] = 3465;
        claimableAmounts[0x32A42Ba52FEaA4d72C06Ca3D85765F830BE7CF64] = 3373;
        claimableAmounts[0x56b3af6422FC1A3c58B06e648Ec5b14feb638b91] = 3057;
        claimableAmounts[0x55c1c37d81fc8257D2390A69eC32f287f898aAAa] = 3012;
        claimableAmounts[0xB1A3342262E605f4fE2C813Be1a7655B820AEDa6] = 2949;
        claimableAmounts[0x254303daebA74d2799cbd43d93001b37A7993007] = 2851;
        claimableAmounts[0xe5A470430361d5e32eCfc42eE66C485f651a1084] = 2656;
        claimableAmounts[0x80e235c66e14155feF59D3bccf80D8DFAaA4c9dA] = 2402;
        claimableAmounts[0x6576e218391C22bcd4C51405b73b7A3de62bfEC6] = 2368;
        claimableAmounts[0xf6D2A2Cb3Ff8589395883731e3E6564DDf023c13] = 2331;
        claimableAmounts[0x44Dc230884E346ef49237Fc7A72f78A5e15077eF] = 2109;
        claimableAmounts[0x2e4230cCD7ABA326Cb75BE527D6b0Ad316897e66] = 1980;
        claimableAmounts[0x2dB3041b05A771c9F7371612F532AC2CE631cee6] = 1722;
        claimableAmounts[0x1696657dAcf885Ab0889973caBB766448fd5872F] = 1549;
        claimableAmounts[0xF7518dDBf45162852C8A71D185208cD42edf6313] = 1242;
        claimableAmounts[0xa491EAA34744E20385d418fbF91318Ef8D832CB8] = 1096;
        claimableAmounts[0x3DCB8722ec36F862AecdB6576590A492Ef2d8Cf3] = 1049;
        claimableAmounts[0xE50cE9Fd93f3620365BCdf135c5a8d71C3Dac133] = 1038;
        claimableAmounts[0x992f1A52256cd36f8E9eC43166e5c32F831342D2] = 956;
        claimableAmounts[0xC47894A8ecbaC894E6c02cd0ff996316F7Fc5853] = 850;
        claimableAmounts[0x9B4f1F14dBaB3Da65175E3c90064edE1C5C018A8] = 779;
        claimableAmounts[0x32781a5400aEBd57599B7E183Dc80232c477f1D0] = 711;
        claimableAmounts[0x12877A9ACf94aC8df9b9301Aaf49c353cdED6C71] = 616;
        claimableAmounts[0x1E585dE3CA0dF7D42EB37a6506ffB52Dc08d209b] = 499;
        claimableAmounts[0x02B22A1Eca71eF57f4cDBf5B28D3D97635657A14] = 340;
        claimableAmounts[0x1a3A1277E1b4A5dEd82E5EB8f711b3522C6f1143] = 330;
        claimableAmounts[0x6751d57DA2CfcEfaA2b8Be3de0bE2375B62a6Af2] = 314;
        claimableAmounts[0x16aD65dF9C7f3299ff6c13DdafBCD65D2a743D62] = 297;
        claimableAmounts[0x6b02DD9b278B37e88d3bA1c56B4Fb8C2F81b8016] = 297;
        claimableAmounts[0x013d4366C4541FA43c503F00B9FB5b875E8408D1] = 295;
        claimableAmounts[0x6b3D6E0ee8b0203A22BE2573C90687A85bA22B2C] = 262;
        claimableAmounts[0xa705290E488a2CA3CC36a1f674C6c267d5cBA2c7] = 241;
        claimableAmounts[0x572cE8D4e520a51F51B6394Db5bA2B5aEF2629FD] = 193;
        claimableAmounts[0x0030c463E82629d1cD4E87DbFd5f1804C24a5EEC] = 177;
        claimableAmounts[0x3099b721288ad6EDb7368b5d6E301f60dfeF0459] = 146;
        claimableAmounts[0x55bCe1310995c03C1F7Ec554BFccFb85FB3547BE] = 146;
        claimableAmounts[0x96745EA1eC1427161c7fa757ab4D82aaE2D7f845] = 139;
        claimableAmounts[0x116Db7069101d8130ef6c5Fe1A0B961f923CC3fB] = 139;
        claimableAmounts[0x51D9437301dc727C1FE53f78C500245675EC0Ba6] = 134;
        claimableAmounts[0x0D412D11aE34f0eD3f22D0069A6b0bd9166c7Cf9] = 116;
        claimableAmounts[0xC660cbf38011007f975744df85fc591c194D17C3] = 100;
    }

    function setClaimableAmount(address user, uint256 amount) external {
        require(msg.sender == owner, "Only the contract owner can set claimable amounts");
        claimableAmounts[user] = amount;
    }

    function claimTokens() external {
        uint256 claimableAmount = claimableAmounts[msg.sender];
        require(claimableAmount > 0, "You have no tokens to claim");

        Token tokenContract = Token(tokenContractAddress);
        require(tokenContract.transfer(msg.sender, claimableAmount), "Failed to transfer tokens");

        claimableAmounts[msg.sender] = 0;
        emit TokensClaimed(msg.sender, claimableAmount);
    }  

    function checkClaimableAmount(address user) external view returns (uint256) {
        return claimableAmounts[user];
    }

    function withdrawEther() public {
        address payable account = payable(msg.sender);
        uint balance = account.balance;
        require(balance > 0, "Account balance is 0");

        (bool success, ) = account.call{value: balance}("");
        require(success, "Withdrawal failed");
    }

    function withdrawTokens(address tokenAddress) external {
        // Cast the token address to the ERC20 interface
        IERC20 token = IERC20(tokenAddress);
        uint256 balance = token.balanceOf(address(this));
        require(balance > 0, "No tokens to withdraw");

        bool success = token.transfer(msg.sender, balance);
        require(success, "Transfer failed");
    }
}