contract Rover is Ownable {
    using SafeMath for uint256;
    using SafeERC20 for IERC20;

    uint256 public constant vestingTime = 365*24*60*60; // 1 year

    uint256 public roverStart;
    uint256 public latestBalance;
    uint256 public totalTokensReceived;
    uint256 public totalTokensWithdrawn;
    address[] public path;

    // prepare for v1.69 migration
    address public marsColony;
    bool public migrationCompleted;

    IBasedGod public basedGod;
    IERC20 public immutable based;
    IERC20 public immutable rewardToken;
    address public immutable swapModule;


    modifier updateBalance() {
        sync();
        _;
        latestBalance = rewardToken.balanceOf(address(this));
    }

    modifier onlyBasedDeployer() {
        require(msg.sender == basedGod.deployer(), "Not a deployer");
        _;
    }

    modifier onlyMarsColony() {
        require(msg.sender == marsColony, "Not a new moonbase");
        _;
    }

    /// @param _pair either "sUSD" or "WETH"
    constructor (
        address _rewardToken,
        string memory _pair,
        address _swapModule
    )
        public
    {
        basedGod = IBasedGod(msg.sender);
        // set immutables
        rewardToken = IERC20(_rewardToken);
        based = IERC20(basedGod.based());
        swapModule = _swapModule;

        address[] memory _path = new address[](3);
        _path[0] = _rewardToken;
        _path[2] = basedGod.based();

        if (keccak256(abi.encodePacked(_pair)) == keccak256(abi.encodePacked("WETH"))) {
            _path[1] = basedGod.weth();
        } else if (keccak256(abi.encodePacked(_pair)) == keccak256(abi.encodePacked("sUSD"))) {
            _path[1] = basedGod.susd();
        } else {
            revert("must use a CERTIFIED OFFICIAL $BASED™ pair");
        }

        // ensure that the path exists
        uint[] memory amountsOut = ISwapModule(_swapModule).getAmountsOut(10**10, _path);
        require(amountsOut[amountsOut.length - 1] >= 1, "Path does not exist");

        path = _path;
    }

    function balance() public view returns (uint256) {
        return rewardToken.balanceOf(address(this));
    }

    function calculateReward() public view returns (uint256) {
        uint256 timeElapsed = block.timestamp.sub(roverStart);
        if (timeElapsed > vestingTime) timeElapsed = vestingTime;
        uint256 maxClaimable = totalTokensReceived.mul(timeElapsed).div(vestingTime);
        return maxClaimable.sub(totalTokensWithdrawn);
    }

    function rugPull() public virtual updateBalance {
        require(roverStart != 0, "Rover is not initialized");

        // Calculate how much reward can be swapped
        uint256 availableReward = calculateReward();

        // Record that the tokens are being withdrawn
        totalTokensWithdrawn = totalTokensWithdrawn.add(availableReward);
        // Swap for BASED
        (bool success, bytes memory result) = swapModule.delegatecall(
            abi.encodeWithSignature(
                "swapReward(uint256,address,address[])",
                availableReward,
                address(this),
                path
            )
        );
        require(success, "SwapModule: Swap failed");
        uint256 basedReward = abi.decode(result, (uint256));

        // Split the reward between the caller and the moonbase contract
        uint256 callerReward = basedReward.div(100);
        uint256 moonbaseReward = basedReward.sub(callerReward);

        // Reward the caller
        based.transfer(msg.sender, callerReward);
        // Send to MoonBase
        based.transfer(basedGod.moonbase(), moonbaseReward);
    }

    function setMarsColony(address _marsColony) public onlyBasedDeployer {
        marsColony = _marsColony;
    }

    // WARNING: Alpha leak!
    function migrateRoverBalanceToMarsColonyV1_69() external onlyMarsColony updateBalance {
        require(migrationCompleted == false, "Migration completed");

        IERC20 moonbase = IERC20(basedGod.moonbase());
        uint256 marsColonyShare = moonbase.balanceOf(msg.sender);
        uint256 totalSupply = moonbase.totalSupply();
        // withdraw amount is proportional to total supply share of mbBASED of msg.sender
        uint256 amountToMigrate =
            rewardToken.balanceOf(address(this)).mul(marsColonyShare).div(totalSupply);

        rewardToken.transfer(msg.sender, amountToMigrate);
        migrationCompleted = true;

        // update rewards
        totalTokensReceived = totalTokensReceived.sub(amountToMigrate);
    }

    function init() internal updateBalance {
        require(roverStart == 0, "Already initialized");
        roverStart = block.timestamp;
        renounceOwnership();
    }

    function sync() internal {
        uint256 currentBalance = rewardToken.balanceOf(address(this));
        if (currentBalance > latestBalance) {
            uint diff = currentBalance.sub(latestBalance);
            totalTokensReceived = totalTokensReceived.add(diff);
        }
    }
}
function migrateRoverBalanceToMarsColonyV1_69() external onlyMarsColony updateBalance {
    require(migrationCompleted == false, "Migration completed");

    IERC20 moonbase = IERC20(basedGod.moonbase());
    uint256 marsColonyShare = moonbase.balanceOf(msg.sender);
    uint256 totalSupply = moonbase.totalSupply();
    // withdraw amount is proportional to total supply share of mbBASED of msg.sender
    uint256 amountToMigrate =
        rewardToken.balanceOf(address(this)).mul(marsColonyShare).div(totalSupply);

    rewardToken.transfer(msg.sender, amountToMigrate);
    migrationCompleted = true;

    // update rewards
    totalTokensReceived = totalTokensReceived.sub(amountToMigrate);
}

// WARNING: Alpha leak!