pragma solidity 0.8.20;

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

import {Gatekeeper} from "../../src/Gatekeeper.sol";

contract GatekeeperTest is Test {
    address constant ALICE       = 0x0000000000000000000000000000000000000001;
    address constant BOB         = 0x0000000000000000000000000000000000000002;
    uint256 constant EXISTENTIAL = 1337;
    uint256 constant INIT_AMOUNT = 69 * 1e18;
    Gatekeeper gatekeeper;

    event Ghosted(bytes32 indexed receiver, uint256 indexed amount);

    function setUp() public {
        gatekeeper = new Gatekeeper(ALICE, 0, EXISTENTIAL, 0, 0, 0);
    }

    function test_correctInitialization() public {
        assertEq(gatekeeper.staking(), ALICE);
        assertEq(gatekeeper.ghostedSupply(), 0);

        Gatekeeper anotherGatekeeper = new Gatekeeper(BOB, INIT_AMOUNT, EXISTENTIAL, 0, 0, 0);
        assertEq(anotherGatekeeper.staking(), BOB);
        assertEq(anotherGatekeeper.ghostedSupply(), INIT_AMOUNT);
        assertEq(anotherGatekeeper.existentialDeposit(), EXISTENTIAL);
        assertEq(anotherGatekeeper.ghostedSupply(), INIT_AMOUNT);
    }

    function test_ghostTokensWork(uint256 ghostAmount) public {
        vm.assume(ghostAmount >= EXISTENTIAL);
        bytes32 receiver = bytes32(abi.encodePacked(ALICE));
        uint256 ghostedSupply = gatekeeper.ghostedSupply();

        vm.prank(ALICE);
        gatekeeper.ghost(receiver, ghostAmount);
        assertEq(gatekeeper.ghostedSupply(), ghostedSupply + ghostAmount);
    }

    function test_couldNotGhostTokensFromArbitraryAddress(address someone) public {
        vm.assume(someone != ALICE);
        bytes32 receiver = bytes32(abi.encodePacked(ALICE));

        vm.expectRevert();
        vm.prank(someone);
        gatekeeper.ghost(receiver, 69);
        assertEq(gatekeeper.ghostedSupply(), 0);
    }

    function test_ghostTokensEmitsEvent(uint256 ghostAmount) public {
        vm.assume(ghostAmount >= EXISTENTIAL);
        bytes32 receiver = bytes32(abi.encodePacked(ALICE));

        vm.expectEmit(true, true, true, false, address(gatekeeper));
        emit Ghosted(receiver, ghostAmount);
        vm.prank(ALICE);
        gatekeeper.ghost(receiver, ghostAmount);
    }

    function test_materializeWork(uint256 ghostAmount) public {
        vm.expectRevert();
        gatekeeper.materialize(ALICE, ghostAmount, 0, 0);
    }

    function test_rotateWork(uint256 aggregatedPublicKey) public {
        vm.expectRevert();
        gatekeeper.rotate(aggregatedPublicKey, 0, 0);
    }

    function test_couldNotBridgeBelowExistential(uint256 amount) public {
        vm.assume(amount < EXISTENTIAL);
        bytes32 receiver = bytes32(abi.encodePacked(ALICE));

        vm.expectRevert();
        vm.prank(ALICE);
        gatekeeper.ghost(receiver, amount);

        assertEq(gatekeeper.ghostedSupply(), 0);

    }
}
