// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

abstract contract Auth {
    address internal owner;
    mapping(address => bool) internal authorizations;

    constructor(address _owner) {
        owner = _owner;
        authorizations[_owner] = true;
    }

    /**
     * Function modifier to require caller to be contract owner
     */
    modifier onlyOwner() {
        require(isOwner(msg.sender), "!OWNER");
        _;
    }

    /**
     * Function modifier to require caller to be authorized
     */
    modifier authorized() {
        require(isAuthorized(msg.sender), "!AUTHORIZED");
        _;
    }

    /**
     * Authorize address. Owner only
     */
    function authorize(address adr) public onlyOwner {
        authorizations[adr] = true;
    }

    /**
     * Remove address' authorization. Owner only
     */
    function unauthorize(address adr) public onlyOwner {
        authorizations[adr] = false;
    }

    /**
     * Check if address is owner
     */
    function isOwner(address account) public view returns (bool) {
        return account == owner;
    }

    /**
     * Return address' authorization status
     */
    function isAuthorized(address adr) public view returns (bool) {
        return authorizations[adr];
    }

    /**
     * Transfer ownership to new address. Caller must be owner. Leaves old owner authorized
     */
    function transferOwnership(address payable adr) public onlyOwner {
        owner = adr;
        authorizations[adr] = true;
        emit OwnershipTransferred(adr);
    }

    event OwnershipTransferred(address owner);
}

interface IERC20 {
    function totalSupply() external view returns (uint256);

    function balanceOf(address account) external view returns (uint256);

    function transfer(address recipient, uint256 amount)
        external
        returns (bool);

    function allowance(address owner, address spender)
        external
        view
        returns (uint256);

    function approve(address spender, uint256 amount) external returns (bool);

    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(
        address indexed owner,
        address indexed spender,
        uint256 value
    );
}

contract JeetsDistributor is Auth {
    IERC20 JEETS;

    uint256 public rewardAmount = 5000000000000000000000;

    uint256 public timeFrame = 86400;

    uint256 public totalShareHolders = 0;

    uint256 public minHoldingAmount = 0;

    struct shareHolderObject {
        address _userAddress;
        uint256 lastClaimTime;
        uint256 holdingFrom;
        bool paused;
    }

    mapping(address => shareHolderObject) public shareHolder;

    constructor() Auth(msg.sender) {
        minHoldingAmount = 50000000 * 10**18;
    }

    function setJeets(address _tokenAddress) external authorized {
        JEETS = IERC20(_tokenAddress);
        authorize(_tokenAddress);
    }

    function setRewardAmount(uint256 _rewardAmount) external authorized {
        rewardAmount = _rewardAmount;
    }

    function setMinHoldingAmount(uint256 _minAmount) external authorized {
        minHoldingAmount = _minAmount;
    }

    function getMinHoldingAmount() external view returns (uint256) {
        return minHoldingAmount;
    }

    function calculateReward() external view returns (uint256) {
        require(
            shareHolder[msg.sender]._userAddress != address(0),
            "Only shareholders"
        );

        uint256 currentTimestamp = block.timestamp;

        uint256 userLastClaimtime = shareHolder[msg.sender].lastClaimTime;

        uint256 holdingFrom = shareHolder[msg.sender].holdingFrom;

        if(holdingFrom<1){
            return 0;
        }

        uint256 rewardTimeFrame = currentTimestamp - userLastClaimtime;

        uint256 rewardPerSecond = rewardAmount / timeFrame;

        uint256 availableReward = rewardPerSecond * rewardTimeFrame;

        return availableReward;
    }

    function calculateUserReward(address _shareHolder)
        external
        view
        authorized
        returns (uint256)
    {
        require(
            shareHolder[_shareHolder]._userAddress != address(0),
            "Only shareholders"
        );

        uint256 currentTimestamp = block.timestamp;

        uint256 userLastClaimtime = shareHolder[msg.sender].lastClaimTime;

        uint256 rewardTimeFrame = currentTimestamp - userLastClaimtime;

        uint256 rewardPerSecond = rewardAmount / timeFrame;

        uint256 availableReward = rewardPerSecond * rewardTimeFrame;

        return availableReward;
    }

    function createShareHolder(address _shareHolder)
        external
        authorized
        returns (bool)
    {
        bool shareHolderStatus = shareHolder[_shareHolder].paused;

        address addr = shareHolder[_shareHolder]._userAddress;

        if (addr == _shareHolder) {
            if (shareHolderStatus == true) {
                shareHolder[_shareHolder].paused = false;
                shareHolder[_shareHolder].lastClaimTime = block.timestamp;
                shareHolder[_shareHolder].holdingFrom = block.timestamp;
            }
        } else {
            shareHolder[_shareHolder] = shareHolderObject({
                _userAddress: _shareHolder,
                lastClaimTime: block.timestamp,
                holdingFrom: block.timestamp,
                paused: false
            });
        }

        totalShareHolders += 1;

        return true;
    }

    function removeShareHolder(address _shareHolder) external authorized {
        shareHolder[_shareHolder].paused = true;
        shareHolder[_shareHolder].holdingFrom = 0;
        totalShareHolders -= 1;
    }

    function claimReward() external {
        address _shareHolder = msg.sender;

        uint256 currentTimestamp = block.timestamp;

        uint256 userLastClaimtime = shareHolder[_shareHolder].lastClaimTime;

        require(
            JEETS.balanceOf(_shareHolder) >= minHoldingAmount,
            "Not qualified"
        );

        require(shareHolder[_shareHolder].paused != true, "Only shareholders");

        shareHolder[_shareHolder].lastClaimTime = block.timestamp;

        uint256 rewardTimeFrame = currentTimestamp - userLastClaimtime;

        uint256 rewardPerSecond = rewardAmount / timeFrame;

        uint256 availableReward = rewardPerSecond * rewardTimeFrame;

        JEETS.transfer(msg.sender, availableReward);
    }

    function getTimeHeld(address _address) external view returns (uint256) {

        shareHolderObject memory _shareHolder = shareHolder[_address];

        uint256 _holdingFrom = _shareHolder.holdingFrom;

        require(_holdingFrom>0,"Address is not a holder");

        uint256 totalTimeHeld = block.timestamp - _holdingFrom;

        return totalTimeHeld;

    }

    function rescueToken(
        address _token,
        address _address,
        uint256 _amount
    ) external authorized {
        IERC20(_token).transfer(_address, _amount);
    }

    function rescueEth() external authorized {
        uint256 balance = address(this).balance;
        payable(msg.sender).transfer(balance);
    }
}