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

import {Test} from "forge-std/Test.sol";
import "../../contracts/dex/DODOPMMIntegration.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MockERC20 is ERC20 {
    constructor(string memory name, string memory symbol) ERC20(name, symbol) {
        _mint(msg.sender, 1_000_000 ether);
    }
}

contract MockPoolFactory {
    address public nextPool;

    function setNextPool(address pool) external {
        nextPool = pool;
    }

    function createDVM(
        address,
        address,
        uint256,
        uint256,
        uint256,
        bool
    ) external view returns (address dvm) {
        return nextPool;
    }
}

contract MockBasicDodoPool {
    address public immutable baseToken;
    address public immutable quoteToken;

    constructor(address baseToken_, address quoteToken_) {
        baseToken = baseToken_;
        quoteToken = quoteToken_;
    }

    function _BASE_TOKEN_() external view returns (address) {
        return baseToken;
    }

    function _QUOTE_TOKEN_() external view returns (address) {
        return quoteToken;
    }

    function getVaultReserve() external pure returns (uint256, uint256) {
        return (1_000_000, 1_000_000);
    }

    function getMidPrice() external pure returns (uint256) {
        return 1e18;
    }

    function _QUOTE_RESERVE_() external pure returns (uint256) {
        return 1_000_000;
    }

    function _BASE_RESERVE_() external pure returns (uint256) {
        return 1_000_000;
    }
}

contract MockStandardDodoPool is MockBasicDodoPool {
    constructor(address baseToken_, address quoteToken_) MockBasicDodoPool(baseToken_, quoteToken_) {}

    function querySellBase(address, uint256 payBaseAmount) external pure returns (uint256, uint256) {
        return (payBaseAmount, 0);
    }

    function querySellQuote(address, uint256 payQuoteAmount) external pure returns (uint256, uint256) {
        return (payQuoteAmount, 0);
    }

    function buyShares(address) external pure returns (uint256, uint256, uint256) {
        return (0, 0, 1);
    }

    function sellBase(address) external pure returns (uint256) {
        return 0;
    }

    function sellQuote(address) external pure returns (uint256) {
        return 0;
    }
}

contract MockPartialDodoPool is MockBasicDodoPool {
    constructor(address baseToken_, address quoteToken_) MockBasicDodoPool(baseToken_, quoteToken_) {}

    function querySellBase(address, uint256) external pure returns (uint256, uint256) {
        revert("query disabled");
    }

    function querySellQuote(address, uint256) external pure returns (uint256, uint256) {
        revert("query disabled");
    }

    function buyShares(address) external pure returns (uint256, uint256, uint256) {
        return (0, 0, 1);
    }

    function sellBase(address) external pure returns (uint256) {
        return 0;
    }

    function sellQuote(address) external pure returns (uint256) {
        return 0;
    }
}

contract DODOPMMIntegrationTest is Test {
    DODOPMMIntegration public integration;
    MockPoolFactory public dvm;
    address public dodoApprove = address(0xD0D0);
    MockERC20 public officialUSDT;
    MockERC20 public officialUSDC;
    MockERC20 public compliantUSDT;
    MockERC20 public compliantUSDC;
    address public admin = address(0xAD);

    function setUp() public {
        officialUSDT = new MockERC20("USDT", "USDT");
        officialUSDC = new MockERC20("USDC", "USDC");
        compliantUSDT = new MockERC20("cUSDT", "cUSDT");
        compliantUSDC = new MockERC20("cUSDC", "cUSDC");
        dvm = new MockPoolFactory();

        officialUSDT.transfer(admin, 1_000 ether);
        officialUSDC.transfer(admin, 1_000 ether);
        compliantUSDT.transfer(admin, 1_000 ether);
        compliantUSDC.transfer(admin, 1_000 ether);

        integration = new DODOPMMIntegration(
            admin,
            address(dvm),
            dodoApprove,
            address(officialUSDT),
            address(officialUSDC),
            address(compliantUSDT),
            address(compliantUSDC)
        );
    }

    function testCreatePoolGeneric() public {
        address baseToken = address(compliantUSDT);
        address quoteToken = address(officialUSDC);
        MockStandardDodoPool poolContract = new MockStandardDodoPool(baseToken, quoteToken);
        dvm.setNextPool(address(poolContract));

        vm.prank(admin);
        address pool = integration.createPool(baseToken, quoteToken, 3, 1e18, 0.5e18, true);

        assertEq(pool, address(poolContract));
        assertEq(integration.pools(baseToken, quoteToken), address(poolContract));
        assertEq(integration.pools(quoteToken, baseToken), address(poolContract));
        assertTrue(integration.isRegisteredPool(address(poolContract)));
        assertFalse(integration.hasStandardPoolSurface(address(poolContract)));
    }

    function testCreatePoolRevertsSameToken() public {
        vm.prank(admin);
        vm.expectRevert("DODOPMMIntegration: same token");
        integration.createPool(address(officialUSDT), address(officialUSDT), 3, 1e18, 0.5e18, true);
    }

    function testCreatePoolRevertsZeroBase() public {
        vm.prank(admin);
        vm.expectRevert("DODOPMMIntegration: zero base");
        integration.createPool(address(0), address(officialUSDT), 3, 1e18, 0.5e18, true);
    }

    function testCreatePoolRevertsWithoutBasicSurface() public {
        dvm.setNextPool(address(0xBEEF));

        vm.prank(admin);
        vm.expectRevert();
        integration.createPool(address(compliantUSDT), address(officialUSDC), 3, 1e18, 0.5e18, false);
    }

    function testImportExistingPoolRecordsMappings() public {
        address baseToken = address(officialUSDT);
        address quoteToken = address(compliantUSDC);
        MockStandardDodoPool pool = new MockStandardDodoPool(baseToken, quoteToken);

        vm.prank(admin);
        integration.importExistingPool(address(pool), baseToken, quoteToken, 3, 1e18, 0.5e18, false);

        assertEq(integration.pools(baseToken, quoteToken), address(pool));
        assertEq(integration.pools(quoteToken, baseToken), address(pool));
        assertTrue(integration.isRegisteredPool(address(pool)));

        DODOPMMIntegration.PoolConfig memory config = integration.getPoolConfig(address(pool));
        assertEq(config.baseToken, baseToken);
        assertEq(config.quoteToken, quoteToken);
    }

    function testImportExistingPoolAcceptsReverseHintAndNormalizes() public {
        address baseToken = address(officialUSDT);
        address quoteToken = address(compliantUSDC);
        MockStandardDodoPool pool = new MockStandardDodoPool(baseToken, quoteToken);

        vm.prank(admin);
        integration.importExistingPool(address(pool), quoteToken, baseToken, 3, 1e18, 0.5e18, false);

        DODOPMMIntegration.PoolConfig memory config = integration.getPoolConfig(address(pool));
        assertEq(config.baseToken, baseToken);
        assertEq(config.quoteToken, quoteToken);
    }

    function testImportExistingPoolRevertsOnMismatch() public {
        MockStandardDodoPool pool = new MockStandardDodoPool(address(officialUSDT), address(compliantUSDC));

        vm.prank(admin);
        vm.expectRevert("DODOPMMIntegration: pool token mismatch");
        integration.importExistingPool(address(pool), address(officialUSDT), address(compliantUSDT), 3, 1e18, 0.5e18, false);
    }

    function testAddLiquidityMarksStandardSurface() public {
        MockStandardDodoPool pool = new MockStandardDodoPool(address(compliantUSDT), address(officialUSDC));
        dvm.setNextPool(address(pool));

        vm.startPrank(admin);
        address createdPool = integration.createPool(address(compliantUSDT), address(officialUSDC), 3, 1e18, 0.5e18, false);
        compliantUSDT.approve(address(integration), 100);
        officialUSDC.approve(address(integration), 100);
        integration.addLiquidity(createdPool, 100, 100);
        vm.stopPrank();

        assertTrue(integration.hasStandardPoolSurface(createdPool));
    }

    function testAddLiquidityRevertsForPartialSurfacePool() public {
        MockPartialDodoPool pool = new MockPartialDodoPool(address(compliantUSDT), address(officialUSDC));
        dvm.setNextPool(address(pool));

        vm.startPrank(admin);
        address createdPool = integration.createPool(address(compliantUSDT), address(officialUSDC), 3, 1e18, 0.5e18, false);
        compliantUSDT.approve(address(integration), 100);
        officialUSDC.approve(address(integration), 100);
        vm.expectRevert("DODOPMMIntegration: pool missing standard DODO surface");
        integration.addLiquidity(createdPool, 100, 100);
        vm.stopPrank();
    }

    function testRefreshPoolSurfaceMarksImportedStandardPool() public {
        MockStandardDodoPool pool = new MockStandardDodoPool(address(compliantUSDT), address(compliantUSDC));

        vm.prank(admin);
        integration.importExistingPool(address(pool), address(compliantUSDT), address(compliantUSDC), 3, 1e18, 0.5e18, false);

        vm.prank(admin);
        bool standardSurface = integration.refreshPoolSurface(address(pool));

        assertTrue(standardSurface);
        assertTrue(integration.hasStandardPoolSurface(address(pool)));
    }
}
