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

import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../../interfaces/IYTPoolManager.sol";
import "../../interfaces/IYTVault.sol";

contract YTRewardRouter is Initializable, UUPSUpgradeable, ReentrancyGuardUpgradeable, PausableUpgradeable {
    using SafeERC20 for IERC20;
    
    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }
    
    error Forbidden();
    error AlreadyInitialized();
    error InvalidAddress();
    error InvalidAmount();
    error InsufficientOutput();
    
    address public gov;
    address public usdy;
    address public ytLP;
    address public ytPoolManager;
    address public ytVault;
    
    event Swap(
        address indexed account,
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        uint256 amountOut
    );
    
    modifier onlyGov() {
        if (msg.sender != gov) revert Forbidden();
        _;
    }
    
    function initialize(
        address _usdy,
        address _ytLP,
        address _ytPoolManager,
        address _ytVault
    ) external initializer {
        if (_usdy == address(0)) revert InvalidAddress();
        if (_ytLP == address(0)) revert InvalidAddress();
        if (_ytPoolManager == address(0)) revert InvalidAddress();
        if (_ytVault == address(0)) revert InvalidAddress();
        
        __ReentrancyGuard_init();
        __UUPSUpgradeable_init();
        __Pausable_init();
        
        gov = msg.sender;

        usdy = _usdy;
        ytLP = _ytLP;
        ytPoolManager = _ytPoolManager;
        ytVault = _ytVault;
    }
    
    function _authorizeUpgrade(address newImplementation) internal override onlyGov {}
    
    function pause() external onlyGov {
        _pause();
    }
    
    function unpause() external onlyGov {
        _unpause();
    }
    
    function addLiquidity(
        address _token,
        uint256 _amount,
        uint256 _minUsdy,
        uint256 _minYtLP
    ) external nonReentrant whenNotPaused returns (uint256) {
        if (_amount == 0) revert InvalidAmount();
        
        address account = msg.sender;
        
        IERC20(_token).transferFrom(account, address(this), _amount);
        IERC20(_token).approve(ytPoolManager, _amount);
        
        uint256 ytLPAmount = IYTPoolManager(ytPoolManager).addLiquidityForAccount(
            address(this),
            account,
            _token,
            _amount,
            _minUsdy,
            _minYtLP
        );
        
        return ytLPAmount;
    }
    
    function removeLiquidity(
        address _tokenOut,
        uint256 _ytLPAmount,
        uint256 _minOut,
        address _receiver
    ) external nonReentrant whenNotPaused returns (uint256) {
        if (_ytLPAmount == 0) revert InvalidAmount();
        
        address account = msg.sender;
        
        uint256 amountOut = IYTPoolManager(ytPoolManager).removeLiquidityForAccount(
            account,
            _tokenOut,
            _ytLPAmount,
            _minOut,
            _receiver
        );
        
        return amountOut;
    }
    
    function swapYT(
        address _tokenIn,
        address _tokenOut,
        uint256 _amountIn,
        uint256 _minOut,
        address _receiver
    ) external nonReentrant whenNotPaused returns (uint256) {
        if (_amountIn == 0) revert InvalidAmount();
        
        address account = msg.sender;
        
        IERC20(_tokenIn).transferFrom(account, ytVault, _amountIn);
        
        uint256 amountOut = IYTVault(ytVault).swap(_tokenIn, _tokenOut, _receiver);
        
        if (amountOut < _minOut) revert InsufficientOutput();
        
        emit Swap(account, _tokenIn, _tokenOut, _amountIn, amountOut);
        
        return amountOut;
    }
    
    function getYtLPPrice() external view returns (uint256) {
        return IYTPoolManager(ytPoolManager).getPrice(true);
    }
    
    function getAccountValue(address _account) external view returns (uint256) {
        uint256 ytLPBalance = IERC20(ytLP).balanceOf(_account);
        uint256 ytLPPrice = IYTPoolManager(ytPoolManager).getPrice(true);
        return ytLPBalance * ytLPPrice / (10 ** 18);
    }
    
    uint256[50] private __gap;
}