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

import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../interfaces/ILiquidityProvider.sol";

/**
 * @title FixedRateLiquidityProvider
 * @notice Narrow Chain 138 prep provider for token pairs that need executable
 *         router-backed conversion while a DODO pool direction is unhealthy.
 */
contract FixedRateLiquidityProvider is ILiquidityProvider, AccessControl {
    using SafeERC20 for IERC20;

    bytes32 public constant RATE_MANAGER_ROLE = keccak256("RATE_MANAGER_ROLE");

    address public immutable tokenIn;
    address public immutable tokenOut;

    uint256 public amountOutPerTokenIn;
    uint256 public slippageBps;
    uint256 public gasEstimate;
    bool public enabled = true;

    event RateUpdated(uint256 amountOutPerTokenIn, uint256 slippageBps);
    event EnabledSet(bool enabled);
    event SwapExecuted(address indexed caller, uint256 amountIn, uint256 amountOut);
    event Swept(address indexed token, address indexed recipient, uint256 amount);

    constructor(
        address admin,
        address tokenIn_,
        address tokenOut_,
        uint256 amountOutPerTokenIn_,
        uint256 slippageBps_,
        uint256 gasEstimate_
    ) {
        require(admin != address(0), "FixedRateLiquidityProvider: zero admin");
        require(tokenIn_ != address(0), "FixedRateLiquidityProvider: zero tokenIn");
        require(tokenOut_ != address(0), "FixedRateLiquidityProvider: zero tokenOut");
        require(tokenIn_ != tokenOut_, "FixedRateLiquidityProvider: same token");
        require(amountOutPerTokenIn_ > 0, "FixedRateLiquidityProvider: zero rate");

        tokenIn = tokenIn_;
        tokenOut = tokenOut_;
        amountOutPerTokenIn = amountOutPerTokenIn_;
        slippageBps = slippageBps_;
        gasEstimate = gasEstimate_;

        _grantRole(DEFAULT_ADMIN_ROLE, admin);
        _grantRole(RATE_MANAGER_ROLE, admin);
    }

    function setRate(uint256 amountOutPerTokenIn_, uint256 slippageBps_) external onlyRole(RATE_MANAGER_ROLE) {
        require(amountOutPerTokenIn_ > 0, "FixedRateLiquidityProvider: zero rate");
        amountOutPerTokenIn = amountOutPerTokenIn_;
        slippageBps = slippageBps_;
        emit RateUpdated(amountOutPerTokenIn_, slippageBps_);
    }

    function setEnabled(bool enabled_) external onlyRole(RATE_MANAGER_ROLE) {
        enabled = enabled_;
        emit EnabledSet(enabled_);
    }

    function setGasEstimate(uint256 gasEstimate_) external onlyRole(RATE_MANAGER_ROLE) {
        gasEstimate = gasEstimate_;
    }

    function getQuote(
        address tokenIn_,
        address tokenOut_,
        uint256 amountIn
    ) external view override returns (uint256 amountOut, uint256 quoteSlippageBps) {
        if (!enabled || tokenIn_ != tokenIn || tokenOut_ != tokenOut || amountIn == 0) {
            return (0, 10_000);
        }

        amountOut = (amountIn * amountOutPerTokenIn) / 1e18;
        quoteSlippageBps = slippageBps;
    }

    function executeSwap(
        address tokenIn_,
        address tokenOut_,
        uint256 amountIn,
        uint256 minAmountOut
    ) external override returns (uint256 amountOut) {
        require(enabled, "FixedRateLiquidityProvider: disabled");
        require(tokenIn_ == tokenIn && tokenOut_ == tokenOut, "FixedRateLiquidityProvider: unsupported pair");
        require(amountIn > 0, "FixedRateLiquidityProvider: zero amount");

        amountOut = (amountIn * amountOutPerTokenIn) / 1e18;
        require(amountOut >= minAmountOut, "FixedRateLiquidityProvider: insufficient output");
        require(IERC20(tokenOut).balanceOf(address(this)) >= amountOut, "FixedRateLiquidityProvider: insufficient liquidity");

        IERC20(tokenIn).safeTransferFrom(msg.sender, address(this), amountIn);
        IERC20(tokenOut).safeTransfer(msg.sender, amountOut);

        emit SwapExecuted(msg.sender, amountIn, amountOut);
    }

    function supportsTokenPair(address tokenIn_, address tokenOut_) external view override returns (bool) {
        return enabled && tokenIn_ == tokenIn && tokenOut_ == tokenOut;
    }

    function providerName() external pure override returns (string memory) {
        return "Fixed Rate Liquidity Provider";
    }

    function estimateGas(address, address, uint256) external view override returns (uint256) {
        return gasEstimate;
    }

    function sweep(address token, address recipient, uint256 amount) external onlyRole(DEFAULT_ADMIN_ROLE) {
        require(recipient != address(0), "FixedRateLiquidityProvider: zero recipient");
        IERC20(token).safeTransfer(recipient, amount);
        emit Swept(token, recipient, amount);
    }
}
