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

import {Test} from "forge-std/Test.sol";

import "../src/UniswapV2Factory.sol";
import "../src/UniswapV2Pair.sol";
import "../src/mock/MockERC20.sol";

import "../src/libraries/UniswapV2Library.sol";

contract UniswapV2FactoryTest is Test {
    UniswapV2Factory public factory;
    MockERC20 public token0;
    MockERC20 public token1;
    MockERC20 public token2;
    MockERC20 public token3;

    function setUp() public {
        factory = new UniswapV2Factory(address(0));

        token0 = new MockERC20("SomeToken0", "ST0", 18);
        token1 = new MockERC20("SomeToken1", "ST1", 18);
        token2 = new MockERC20("SomeToken2", "ST2", 9);
        token3 = new MockERC20("SomeToken3", "ST3", 6);
    }

    function testCreatePair() public {
        address tokenPair = factory.createPair(address(token0), address(token1));
        (address _token0, address _token1) = UniswapV2Library.sortTokens(
            address(token0),
            address(token1)
        );

        assertEq(factory.allPairsLength(), 1);
        assertEq(factory.getPair(address(token0), address(token1)), tokenPair);
        assertEq(UniswapV2Pair(tokenPair).token0(), _token0);
        assertEq(UniswapV2Pair(tokenPair).token1(), _token1);
    }

    function testCreatePairReverse() public {
        address tokenPair = factory.createPair(address(token1), address(token0));
        (address _token0, address _token1) = UniswapV2Library.sortTokens(
            address(token0),
            address(token1)
        );

        assertEq(factory.allPairsLength(), 1);
        assertEq(factory.getPair(address(token0), address(token1)), tokenPair);
        assertEq(UniswapV2Pair(tokenPair).token0(), _token0);
        assertEq(UniswapV2Pair(tokenPair).token1(), _token1);
    }

    function testCreatePairMultipleTokens() public {
        address tokenPair0 = factory.createPair(address(token0), address(token1));
        address tokenPair1 = factory.createPair(address(token2), address(token3));

        (address _token0, address _token1) = UniswapV2Library.sortTokens(address(token0), address(token1));
        (address _token2, address _token3) = UniswapV2Library.sortTokens(address(token2), address(token3));

        assertEq(factory.allPairsLength(), 2);
        assertEq(factory.getPair(address(token0), address(token1)), tokenPair0);
        assertEq(factory.getPair(address(token2), address(token3)), tokenPair1);
        assertEq(UniswapV2Pair(tokenPair0).token0(), _token0);
        assertEq(UniswapV2Pair(tokenPair0).token1(), _token1);
        assertEq(UniswapV2Pair(tokenPair1).token0(), _token2);
        assertEq(UniswapV2Pair(tokenPair1).token1(), _token3);
    }

    function testCreatePairChained() public {
        address tokenPair0 = factory.createPair(address(token0), address(token1));
        address tokenPair1 = factory.createPair(address(token1), address(token2));
        address tokenPair2 = factory.createPair(address(token2), address(token3));

        assertEq(factory.allPairsLength(), 3);
        assertEq(factory.getPair(address(token0), address(token1)), tokenPair0);
        assertEq(factory.getPair(address(token1), address(token2)), tokenPair1);
        assertEq(factory.getPair(address(token2), address(token3)), tokenPair2);
    }

    function testCreatePairIdenticalTokens() public {
        vm.expectRevert();
        factory.createPair(address(token0), address(token0));
    }

    function testCreatePairInvalidToken() public {
        vm.expectRevert();
        factory.createPair(address(0), address(token1));

        vm.expectRevert();
        factory.createPair(address(token0), address(0));
    }

    function testCreatePairDuplicatePair() public {
        factory.createPair(address(token0), address(token1));
        vm.expectRevert();
        factory.createPair(address(token0), address(token1));
    }
}