// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import { Test } from "forge-std/Test.sol";
import { MultisigGovernanceV3 } from "../../contracts/governance/MultisigGovernanceV3.sol";
import { StreetCredV3 } from "../../contracts/reputation/StreetCredV3.sol";

contract PausableGovernanceTargetMock {
    bytes32 public constant GOVERNANCE_POLICY_MODULE_ID = keccak256("Vaultfire.BaseV3.PilotController");
    address public owner;
    bool public paused;
    uint256 public value;

    error NotOwner();
    error OwnerAlreadySet();

    modifier onlyOwner() {
        if (msg.sender != owner) revert NotOwner();
        _;
    }

    function setOwnerOnce(address newOwner) external {
        if (owner != address(0)) revert OwnerAlreadySet();
        owner = newOwner;
    }

    function pause() external onlyOwner {
        paused = true;
    }

    function unpause() external onlyOwner {
        paused = false;
    }

    function requiredChainId() external view returns (uint256) {
        return block.chainid;
    }

    function maxGlobalCap() external pure returns (uint256) {
        return type(uint256).max;
    }

    function maxPerPositionCap() external pure returns (uint256) {
        return type(uint256).max;
    }

    function setCaps(uint256 newValue, uint256) external onlyOwner {
        value = newValue;
    }
}

contract MultisigGovernanceV3Test is Test {
    address internal constant SIGNER_A = address(0xA11CE);
    address internal constant SIGNER_B = address(0xB0B);
    address internal constant SIGNER_C = address(0xCA101);
    address internal constant SIGNER_D = address(0xD11CE);
    address internal constant SIGNER_E = address(0xE11CE);
    address internal constant GUARDIAN = address(0x600D);
    address internal constant NEW_GUARDIAN = address(0x600E);
    address internal constant NEW_SIGNER_A = address(0xA12);
    address internal constant NEW_SIGNER_B = address(0xB12);

    uint256 internal constant TIMELOCK = 1 days;
    uint256 internal constant ROTATION_DELAY = 2 days;

    MultisigGovernanceV3 internal governance;
    PausableGovernanceTargetMock internal pauseTarget;

    function setUp() external {
        pauseTarget = new PausableGovernanceTargetMock();
        address[] memory targets = new address[](1);
        targets[0] = address(pauseTarget);

        governance = new MultisigGovernanceV3(
            _signers(), 2, TIMELOCK, TIMELOCK, GUARDIAN, ROTATION_DELAY, targets, new address[](0), new bytes32[](0)
        );
        pauseTarget.setOwnerOnce(address(governance));
        vm.prank(address(governance));
        governance.setCallPolicy(
            address(pauseTarget), pauseTarget.setCaps.selector, true, 0, address(pauseTarget).codehash
        );
    }

    function testConstructorRejectsZeroAndDuplicateSigners() external {
        address[] memory duplicateSigners = new address[](2);
        duplicateSigners[0] = SIGNER_A;
        duplicateSigners[1] = SIGNER_A;
        address[] memory noTargets = new address[](0);

        vm.expectRevert(MultisigGovernanceV3.InvalidConfig.selector);
        new MultisigGovernanceV3(
            duplicateSigners,
            2,
            TIMELOCK,
            TIMELOCK,
            GUARDIAN,
            ROTATION_DELAY,
            noTargets,
            new address[](0),
            new bytes32[](0)
        );

        address[] memory zeroSigner = new address[](1);
        zeroSigner[0] = address(0);
        vm.expectRevert(MultisigGovernanceV3.InvalidConfig.selector);
        new MultisigGovernanceV3(
            zeroSigner, 1, TIMELOCK, TIMELOCK, GUARDIAN, ROTATION_DELAY, noTargets, new address[](0), new bytes32[](0)
        );
    }

    function testConstructorRejectsUnsafeTimelockAndGuardianConfiguration() external {
        address[] memory noTargets = new address[](0);

        vm.expectRevert(MultisigGovernanceV3.InvalidConfig.selector);
        new MultisigGovernanceV3(
            _signers(), 2, 0, 0, GUARDIAN, ROTATION_DELAY, noTargets, new address[](0), new bytes32[](0)
        );

        vm.expectRevert(MultisigGovernanceV3.InvalidConfig.selector);
        new MultisigGovernanceV3(
            _signers(),
            2,
            TIMELOCK - 1,
            TIMELOCK,
            GUARDIAN,
            ROTATION_DELAY,
            noTargets,
            new address[](0),
            new bytes32[](0)
        );

        vm.expectRevert(MultisigGovernanceV3.InvalidConfig.selector);
        new MultisigGovernanceV3(
            _signers(), 2, TIMELOCK, TIMELOCK, SIGNER_A, ROTATION_DELAY, noTargets, new address[](0), new bytes32[](0)
        );

        address[] memory excessiveTargets = new address[](governance.MAX_GUARDIAN_PAUSE_TARGETS() + 1);
        vm.expectRevert(MultisigGovernanceV3.InvalidConfig.selector);
        new MultisigGovernanceV3(
            _signers(),
            2,
            TIMELOCK,
            TIMELOCK,
            GUARDIAN,
            ROTATION_DELAY,
            excessiveTargets,
            new address[](0),
            new bytes32[](0)
        );
    }

    function testOnlySelfCanChangeConfigurationAndTimelockCannotGoBelowFloor() external {
        vm.expectRevert(MultisigGovernanceV3.NotSelf.selector);
        governance.setTimelock(TIMELOCK);

        vm.expectPartialRevert(MultisigGovernanceV3.PolicyDenied.selector);
        vm.prank(SIGNER_A);
        governance.propose(address(governance), 0, abi.encodeCall(governance.setTimelock, (TIMELOCK - 1)));
        assertEq(governance.timelockSeconds(), TIMELOCK);
    }

    function testProposalUsesThresholdSnapshotAndSignerChangeInvalidatesOldPendingProposals() external {
        uint256 staleId = _propose(address(pauseTarget), abi.encodeCall(pauseTarget.setCaps, (7, 1)));

        address[] memory newSigners = _replacementSigners();
        uint256 configId = _propose(address(governance), abi.encodeCall(governance.setSigners, (newSigners, 3)));
        _confirmByThreshold(configId);
        vm.warp(block.timestamp + TIMELOCK);
        vm.prank(SIGNER_A);
        governance.execute(configId);

        assertTrue(governance.isSigner(NEW_SIGNER_A));
        assertFalse(governance.isSigner(SIGNER_A));
        assertEq(governance.threshold(), 3);

        vm.expectRevert(MultisigGovernanceV3.StaleProposal.selector);
        vm.prank(NEW_SIGNER_A);
        governance.execute(staleId);
        assertEq(pauseTarget.value(), 0);
    }

    function testGuardianCanOnlyPauseAnApprovedTarget() external {
        vm.prank(GUARDIAN);
        governance.guardianPause(address(pauseTarget));
        assertTrue(pauseTarget.paused());

        PausableGovernanceTargetMock unapprovedTarget = new PausableGovernanceTargetMock();
        vm.expectRevert(MultisigGovernanceV3.GuardianTargetNotAllowed.selector);
        vm.prank(GUARDIAN);
        governance.guardianPause(address(unapprovedTarget));

        vm.expectRevert(MultisigGovernanceV3.NotSigner.selector);
        vm.prank(GUARDIAN);
        governance.propose(address(pauseTarget), 0, abi.encodeCall(pauseTarget.unpause, ()));
    }

    function testPauseTargetCanOnlyBeAddedThroughTimelockedGovernance() external {
        PausableGovernanceTargetMock laterTarget = new PausableGovernanceTargetMock();
        laterTarget.setOwnerOnce(address(governance));
        vm.expectRevert(MultisigGovernanceV3.NotSelf.selector);
        governance.addGuardianPauseTarget(address(laterTarget));

        uint256 pinId = _propose(
            address(governance),
            abi.encodeCall(governance.pinCanonicalAddress, (address(laterTarget), address(laterTarget).codehash))
        );
        _confirmByThreshold(pinId);
        vm.warp(block.timestamp + TIMELOCK);
        vm.prank(SIGNER_A);
        governance.execute(pinId);

        uint256 id =
            _propose(address(governance), abi.encodeCall(governance.addGuardianPauseTarget, (address(laterTarget))));
        _confirmByThreshold(id);
        vm.warp(block.timestamp + TIMELOCK);
        vm.prank(SIGNER_A);
        governance.execute(id);

        assertTrue(governance.isGuardianPauseTarget(address(laterTarget)));
        assertEq(governance.guardianPauseTargetCount(), 2);
        vm.prank(GUARDIAN);
        governance.guardianPause(address(laterTarget));
        assertTrue(laterTarget.paused());
    }

    function testGuardianPauseTargetAppendFailsClosedForNonCodeAndDuplicateTargets() external {
        address noCodeTarget = address(0xBAD);
        vm.expectPartialRevert(MultisigGovernanceV3.PolicyDenied.selector);
        vm.prank(SIGNER_A);
        governance.propose(address(governance), 0, abi.encodeCall(governance.addGuardianPauseTarget, (noCodeTarget)));
        assertFalse(governance.isGuardianPauseTarget(noCodeTarget));
        assertEq(governance.guardianPauseTargetCount(), 1);

        uint256 duplicateId =
            _propose(address(governance), abi.encodeCall(governance.addGuardianPauseTarget, (address(pauseTarget))));
        _confirmByThreshold(duplicateId);
        vm.warp(block.timestamp + TIMELOCK);

        vm.expectRevert(MultisigGovernanceV3.CallFailed.selector);
        vm.prank(SIGNER_A);
        governance.execute(duplicateId);
        assertEq(governance.guardianPauseTargetCount(), 1);
    }

    function testFuzzGuardianOrArbitraryCallerCannotAppendPauseTarget(address caller, address target) external {
        vm.assume(caller != address(governance));
        vm.expectRevert(MultisigGovernanceV3.NotSelf.selector);
        vm.prank(caller);
        governance.addGuardianPauseTarget(target);
        assertEq(governance.guardianPauseTargetCount(), 1);
    }

    function testGuardianRotationRequiresGovernanceCooldownAndAcceptance() external {
        uint256 id = _propose(address(governance), abi.encodeCall(governance.proposeGuardian, (NEW_GUARDIAN)));
        _confirmByThreshold(id);
        vm.warp(block.timestamp + TIMELOCK);
        vm.prank(SIGNER_A);
        governance.execute(id);

        assertEq(governance.pendingGuardian(), NEW_GUARDIAN);
        vm.expectRevert(MultisigGovernanceV3.GuardianRotationNotReady.selector);
        vm.prank(NEW_GUARDIAN);
        governance.acceptGuardian();

        vm.warp(block.timestamp + ROTATION_DELAY);
        vm.prank(NEW_GUARDIAN);
        governance.acceptGuardian();
        assertEq(governance.guardian(), NEW_GUARDIAN);
        assertEq(governance.pendingGuardian(), address(0));

        vm.expectRevert(MultisigGovernanceV3.NotGuardian.selector);
        vm.prank(GUARDIAN);
        governance.guardianPause(address(pauseTarget));
    }

    function testProposalBoundsAndConfirmationRules() external {
        bytes memory oversizedData = new bytes(governance.MAX_PROPOSAL_DATA_LENGTH() + 1);
        vm.expectRevert(MultisigGovernanceV3.ProposalTooLarge.selector);
        vm.prank(SIGNER_A);
        governance.propose(address(pauseTarget), 0, oversizedData);

        vm.expectRevert(MultisigGovernanceV3.InvalidProposal.selector);
        vm.prank(SIGNER_A);
        governance.propose(address(0), 0, "");

        uint256 id = _propose(address(pauseTarget), abi.encodeCall(pauseTarget.setCaps, (42, 1)));
        vm.prank(SIGNER_A);
        governance.confirm(id);
        vm.expectRevert(MultisigGovernanceV3.AlreadyConfirmed.selector);
        vm.prank(SIGNER_A);
        governance.confirm(id);

        vm.expectRevert(MultisigGovernanceV3.ThresholdNotMet.selector);
        vm.prank(SIGNER_A);
        governance.execute(id);
    }

    function testDefaultDenyAndCommittedSelectorValuePolicy() external {
        PausableGovernanceTargetMock target = new PausableGovernanceTargetMock();
        target.setOwnerOnce(address(governance));
        bytes memory call = abi.encodeCall(target.setCaps, (77, 1));

        vm.expectRevert(
            abi.encodeWithSelector(
                MultisigGovernanceV3.PolicyDenied.selector, address(target), target.setCaps.selector, uint256(0)
            )
        );
        vm.prank(SIGNER_A);
        governance.propose(address(target), 0, call);

        vm.prank(address(governance));
        governance.pinCanonicalAddress(address(target), address(target).codehash);
        vm.expectRevert(MultisigGovernanceV3.InvalidConfig.selector);
        vm.prank(address(governance));
        governance.setCallPolicy(address(target), target.setCaps.selector, true, 0, address(target).codehash);

        vm.expectRevert(
            abi.encodeWithSelector(
                MultisigGovernanceV3.PolicyDenied.selector, address(target), target.setCaps.selector, uint256(1)
            )
        );
        vm.prank(SIGNER_A);
        governance.propose(address(target), 1, call);
    }

    function test_exposesExactCanonicalPolicyDigest() external view {
        assertEq(
            governance.governancePolicyCommitment(),
            hex"c94784bc91de3b01c7ea27f945fbd27a3fb9a25f345bf681fc91fe119aee8eaf"
        );
    }

    function test_rejectsTrailingMalformedAndForbiddenArguments() external {
        bytes memory canonical = abi.encodeCall(pauseTarget.setCaps, (7, 1));
        bytes memory trailing = abi.encodePacked(canonical, bytes32(uint256(1)));
        vm.expectPartialRevert(MultisigGovernanceV3.PolicyDenied.selector);
        vm.prank(SIGNER_A);
        governance.propose(address(pauseTarget), 0, trailing);

        bytes memory malformedBool = abi.encodePacked(
            governance.setCallPolicy.selector,
            bytes32(uint256(uint160(address(pauseTarget)))),
            bytes32(pauseTarget.setCaps.selector),
            bytes32(uint256(2)),
            bytes32(0)
        );
        vm.expectPartialRevert(MultisigGovernanceV3.PolicyDenied.selector);
        vm.prank(SIGNER_A);
        governance.propose(address(governance), 0, malformedBool);

        bytes memory noncanonicalAddress =
            abi.encodePacked(governance.addGuardianPauseTarget.selector, bytes32(type(uint256).max));
        vm.expectPartialRevert(MultisigGovernanceV3.PolicyDenied.selector);
        vm.prank(SIGNER_A);
        governance.propose(address(governance), 0, noncanonicalAddress);
    }

    function test_policyDriftInvalidatesPendingProposalAtExecution() external {
        bytes memory call = abi.encodeCall(pauseTarget.setCaps, (7, 1));
        uint256 id = _propose(address(pauseTarget), call);
        _confirmByThreshold(id);
        vm.prank(address(governance));
        governance.setCallPolicy(
            address(pauseTarget), pauseTarget.setCaps.selector, false, 0, address(pauseTarget).codehash
        );
        vm.warp(block.timestamp + TIMELOCK);
        vm.expectRevert(MultisigGovernanceV3.StaleProposal.selector);
        vm.prank(SIGNER_A);
        governance.execute(id);
    }

    function test_retiredInformationalModuleCannotBeAddedToCanonicalPolicy() external {
        StreetCredV3 retired = new StreetCredV3(address(governance));
        vm.expectRevert(MultisigGovernanceV3.InvalidConfig.selector);
        vm.prank(address(governance));
        governance.setCallPolicy(address(retired), bytes4(0x5c0204d9), true, 0, address(retired).codehash);
    }

    function test_spoofedMarkerTargetCannotReuseCanonicalTargetPolicy() external {
        PausableGovernanceTargetMock hostileLookalike = new PausableGovernanceTargetMock();
        hostileLookalike.setOwnerOnce(address(governance));

        vm.expectRevert(MultisigGovernanceV3.InvalidConfig.selector);
        vm.prank(address(governance));
        governance.setCallPolicy(
            address(hostileLookalike), hostileLookalike.setCaps.selector, true, 0, address(pauseTarget).codehash
        );

        vm.expectPartialRevert(MultisigGovernanceV3.PolicyDenied.selector);
        vm.prank(SIGNER_A);
        governance.propose(
            address(hostileLookalike), 0, abi.encodeCall(hostileLookalike.setCaps, (type(uint256).max, 1))
        );
    }

    function test_runtimeCodeDriftInvalidatesProposalAtExecution() external {
        uint256 id = _propose(address(pauseTarget), abi.encodeCall(pauseTarget.setCaps, (7, 1)));
        _confirmByThreshold(id);
        vm.etch(address(pauseTarget), hex"60006000");
        vm.warp(block.timestamp + TIMELOCK);

        vm.expectPartialRevert(MultisigGovernanceV3.PolicyDenied.selector);
        vm.prank(SIGNER_A);
        governance.execute(id);
    }

    function testGovernedTargetRejectsDirectCallAndExecutesOnlyThroughTimelockedProposal() external {
        vm.expectRevert(PausableGovernanceTargetMock.NotOwner.selector);
        vm.prank(SIGNER_A);
        pauseTarget.setCaps(9, 1);

        uint256 id = _propose(address(pauseTarget), abi.encodeCall(pauseTarget.setCaps, (9, 1)));
        vm.prank(SIGNER_A);
        governance.confirm(id);
        vm.prank(SIGNER_B);
        governance.confirm(id);

        vm.expectRevert(MultisigGovernanceV3.TimelockNotElapsed.selector);
        vm.prank(SIGNER_A);
        governance.execute(id);
        assertEq(pauseTarget.value(), 0);

        vm.warp(block.timestamp + TIMELOCK);
        vm.prank(SIGNER_A);
        governance.execute(id);
        assertEq(pauseTarget.value(), 9);
    }

    function testProposalSnapshotsThresholdAtCreation() external {
        uint256 firstId = _propose(address(pauseTarget), abi.encodeCall(pauseTarget.setCaps, (99, 1)));
        (,,,,, uint256 requiredConfirmations,,,,) = governance.proposals(firstId);
        assertEq(requiredConfirmations, 2);
    }

    function testSingleSignerCannotCancelThresholdConfirmedThreeOfFive() external {
        address[] memory noTargets = new address[](0);
        MultisigGovernanceV3 threeOfFive = new MultisigGovernanceV3(
            _fiveSigners(), 3, TIMELOCK, TIMELOCK, GUARDIAN, ROTATION_DELAY, noTargets, _oneTarget(), _oneHash()
        );
        vm.prank(address(threeOfFive));
        threeOfFive.setCallPolicy(
            address(pauseTarget), pauseTarget.setCaps.selector, true, 0, address(pauseTarget).codehash
        );

        vm.prank(SIGNER_A);
        uint256 id = threeOfFive.propose(address(pauseTarget), 0, abi.encodeCall(pauseTarget.setCaps, (7, 1)));
        vm.prank(SIGNER_A);
        threeOfFive.confirm(id);
        vm.prank(SIGNER_B);
        threeOfFive.confirm(id);
        vm.prank(SIGNER_C);
        threeOfFive.confirm(id);

        vm.prank(SIGNER_D);
        threeOfFive.cancel(id);

        (,,,,,,,, bool cancelled,) = threeOfFive.proposals(id);
        assertFalse(cancelled);
        assertTrue(threeOfFive.cancellationConfirmed(id, SIGNER_D));
        assertEq(threeOfFive.cancellationConfirmations(id), 1);
    }

    function testThresholdSignersCanCancelThresholdConfirmedThreeOfFive() external {
        address[] memory noTargets = new address[](0);
        MultisigGovernanceV3 threeOfFive = new MultisigGovernanceV3(
            _fiveSigners(), 3, TIMELOCK, TIMELOCK, GUARDIAN, ROTATION_DELAY, noTargets, _oneTarget(), _oneHash()
        );
        vm.prank(address(threeOfFive));
        threeOfFive.setCallPolicy(
            address(pauseTarget), pauseTarget.setCaps.selector, true, 0, address(pauseTarget).codehash
        );

        vm.prank(SIGNER_A);
        uint256 id = threeOfFive.propose(address(pauseTarget), 0, abi.encodeCall(pauseTarget.setCaps, (7, 1)));
        vm.prank(SIGNER_A);
        threeOfFive.confirm(id);
        vm.prank(SIGNER_B);
        threeOfFive.confirm(id);
        vm.prank(SIGNER_C);
        threeOfFive.confirm(id);

        vm.prank(SIGNER_A);
        threeOfFive.cancel(id);
        vm.prank(SIGNER_B);
        threeOfFive.cancel(id);
        vm.prank(SIGNER_C);
        threeOfFive.cancel(id);

        (,,,,,,,, bool cancelled,) = threeOfFive.proposals(id);
        assertTrue(cancelled);
        assertEq(threeOfFive.cancellationConfirmations(id), 3);
    }

    function testCancellationConfirmationCannotReplay() external {
        uint256 id = _propose(address(pauseTarget), abi.encodeCall(pauseTarget.setCaps, (7, 1)));

        vm.prank(SIGNER_A);
        governance.cancel(id);

        vm.expectRevert(MultisigGovernanceV3.AlreadyCancellationConfirmed.selector);
        vm.prank(SIGNER_A);
        governance.cancel(id);
        assertEq(governance.cancellationConfirmations(id), 1);
    }

    function testCancellationIsNotTimelockedAndPreventsExecutionDuringTimelock() external {
        uint256 id = _propose(address(pauseTarget), abi.encodeCall(pauseTarget.setCaps, (7, 1)));
        _confirmByThreshold(id);

        vm.prank(SIGNER_A);
        governance.cancel(id);
        vm.prank(SIGNER_B);
        governance.cancel(id);

        vm.expectRevert(MultisigGovernanceV3.Cancelled.selector);
        vm.prank(SIGNER_C);
        governance.execute(id);
    }

    function testCannotCancelExecutedProposal() external {
        uint256 id = _propose(address(pauseTarget), abi.encodeCall(pauseTarget.setCaps, (7, 1)));
        _confirmByThreshold(id);
        vm.warp(block.timestamp + TIMELOCK);
        vm.prank(SIGNER_A);
        governance.execute(id);

        vm.expectRevert(MultisigGovernanceV3.AlreadyExecuted.selector);
        vm.prank(SIGNER_C);
        governance.cancel(id);
    }

    function testExpiredProposalCannotExecuteButCanStillBeCancelled() external {
        uint256 id = _propose(address(pauseTarget), abi.encodeCall(pauseTarget.setCaps, (7, 1)));
        _confirmByThreshold(id);
        vm.warp(block.timestamp + 365 days);

        vm.expectRevert(MultisigGovernanceV3.ProposalExpired.selector);
        vm.prank(SIGNER_A);
        governance.execute(id);

        vm.prank(SIGNER_A);
        governance.cancel(id);
        vm.prank(SIGNER_B);
        governance.cancel(id);

        (,,,,,,,, bool cancelled,) = governance.proposals(id);
        assertTrue(cancelled);
    }

    function testSignerRotationInvalidatesPartialCancellationConfirmation() external {
        uint256 staleId = _propose(address(pauseTarget), abi.encodeCall(pauseTarget.setCaps, (7, 1)));
        vm.prank(SIGNER_A);
        governance.cancel(staleId);

        address[] memory newSigners = _replacementSigners();
        uint256 configId = _propose(address(governance), abi.encodeCall(governance.setSigners, (newSigners, 3)));
        _confirmByThreshold(configId);
        vm.warp(block.timestamp + TIMELOCK);
        vm.prank(SIGNER_A);
        governance.execute(configId);

        vm.expectRevert(MultisigGovernanceV3.StaleProposal.selector);
        vm.prank(NEW_SIGNER_A);
        governance.cancel(staleId);
        assertEq(governance.cancellationConfirmations(staleId), 1);
    }

    function testFuzzNonSignerCannotConfirmCancellation(address caller) external {
        vm.assume(caller != SIGNER_A && caller != SIGNER_B && caller != SIGNER_C);
        uint256 id = _propose(address(pauseTarget), abi.encodeCall(pauseTarget.setCaps, (7, 1)));

        vm.expectRevert(MultisigGovernanceV3.NotSigner.selector);
        vm.prank(caller);
        governance.cancel(id);
        assertEq(governance.cancellationConfirmations(id), 0);
    }

    function testGuardianCannotConfirmExecuteOrCallGovernedTarget() external {
        uint256 id = _propose(address(pauseTarget), abi.encodeCall(pauseTarget.setCaps, (5, 1)));

        vm.expectRevert(MultisigGovernanceV3.NotSigner.selector);
        vm.prank(GUARDIAN);
        governance.confirm(id);

        vm.expectRevert(MultisigGovernanceV3.NotSigner.selector);
        vm.prank(GUARDIAN);
        governance.execute(id);

        vm.expectRevert(PausableGovernanceTargetMock.NotOwner.selector);
        vm.prank(GUARDIAN);
        pauseTarget.setCaps(5, 1);
    }

    function _propose(address target, bytes memory data) internal returns (uint256 id) {
        vm.prank(SIGNER_A);
        id = governance.propose(target, 0, data);
    }

    function _confirmByThreshold(uint256 id) internal {
        vm.prank(SIGNER_A);
        governance.confirm(id);
        vm.prank(SIGNER_B);
        governance.confirm(id);
    }

    function _signers() internal pure returns (address[] memory initialSigners) {
        initialSigners = new address[](3);
        initialSigners[0] = SIGNER_A;
        initialSigners[1] = SIGNER_B;
        initialSigners[2] = SIGNER_C;
    }

    function _replacementSigners() internal pure returns (address[] memory values) {
        values = new address[](5);
        values[0] = NEW_SIGNER_A;
        values[1] = NEW_SIGNER_B;
        values[2] = address(0xC12);
        values[3] = address(0xD12);
        values[4] = address(0xE12);
    }

    function _fiveSigners() internal pure returns (address[] memory initialSigners) {
        initialSigners = new address[](5);
        initialSigners[0] = SIGNER_A;
        initialSigners[1] = SIGNER_B;
        initialSigners[2] = SIGNER_C;
        initialSigners[3] = SIGNER_D;
        initialSigners[4] = SIGNER_E;
    }

    function _oneTarget() internal view returns (address[] memory values) {
        values = new address[](1);
        values[0] = address(pauseTarget);
    }

    function _oneHash() internal view returns (bytes32[] memory values) {
        values = new bytes32[](1);
        values[0] = address(pauseTarget).codehash;
    }
}
