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

import {Test} from "forge-std/Test.sol";
import {OMNLJurisdictionPolicyRegistry} from "../../contracts/hybx-omnl/OMNLJurisdictionPolicyRegistry.sol";
import {OMNLNotaryRegistry} from "../../contracts/hybx-omnl/OMNLNotaryRegistry.sol";
import {OMNLComplianceMultisig} from "../../contracts/hybx-omnl/OMNLComplianceMultisig.sol";

contract NotaryAndMultisigTest is Test {
    uint256 internal constant PK1 = 0xA11CE;
    uint256 internal constant PK2 = 0xB0B;

    OMNLJurisdictionPolicyRegistry public jurisdiction;
    OMNLNotaryRegistry public notary;
    OMNLComplianceMultisig public multisig;

    bytes32 internal constant JUR_ID = keccak256("ID");
    bytes32 internal constant ROW_ID = keccak256("ID-OMNL-001");
    bytes32 internal constant CONTENT = keccak256("package-hash");

    function setUp() public {
        jurisdiction = new OMNLJurisdictionPolicyRegistry(address(this));
        notary = new OMNLNotaryRegistry(address(this), address(jurisdiction));
        multisig = new OMNLComplianceMultisig(address(this), address(notary));

        jurisdiction.publishPolicy(JUR_ID, keccak256("policy-v1"), 2, 2, 2, true);

        address s1 = vm.addr(PK1);
        address s2 = vm.addr(PK2);
        notary.setNotarySigner(s1, true);
        notary.setNotarySigner(s2, true);
        notary.setGlobalNotaryThreshold(2);

        multisig.grantRole(multisig.ADMIN_SIGNER_ROLE(), s1);
        multisig.grantRole(multisig.ADMIN_SIGNER_ROLE(), s2);
        multisig.grantRole(multisig.PROPOSER_ROLE(), address(this));
        multisig.setAdminThreshold(2);
    }

    function testNotarizeAttested() public {
        bytes[] memory sigs = _notarySigs(0);
        vm.prank(address(0xBEEF));
        bytes32 key = notary.notarizeAttested(
            JUR_ID, ROW_ID, CONTENT, bytes32(uint256(1)), bytes32(uint256(2)), OMNLNotaryRegistry.NotarizationKind.EvidencePackage, 0, sigs
        );
        assertTrue(notary.isNotarized(JUR_ID, ROW_ID, CONTENT));
        OMNLNotaryRegistry.NotarizationRecord memory r = notary.getNotarization(key);
        assertEq(r.version, 1);
    }

    function testMultisigGatedExecution() public {
        bytes[] memory sigs = _notarySigs(0);
        notary.notarizeAttested(
            JUR_ID,
            ROW_ID,
            CONTENT,
            bytes32(uint256(1)),
            bytes32(uint256(2)),
            OMNLNotaryRegistry.NotarizationKind.EvidencePackage,
            0,
            sigs
        );

        Target t = new Target();
        uint256 executionId = multisig.submitExecution(JUR_ID, address(t), 0, abi.encodeCall(Target.touch, ()), true, CONTENT, ROW_ID);
        vm.prank(vm.addr(PK1));
        multisig.confirmExecution(executionId);
        multisig.executePending(executionId);
        assertTrue(t.touched());
    }

    function _notarySigs(uint256 nonce) internal view returns (bytes[] memory sigs) {
        sigs = new bytes[](2);
        sigs[0] = _signNotary(PK1, nonce);
        sigs[1] = _signNotary(PK2, nonce);
    }

    function _signNotary(uint256 pk, uint256 nonce) internal view returns (bytes memory) {
        bytes32 digest = keccak256(
            abi.encode(
                notary.NOTARIZATION_TYPEHASH(),
                block.chainid,
                address(notary),
                JUR_ID,
                ROW_ID,
                CONTENT,
                bytes32(uint256(1)),
                bytes32(uint256(2)),
                nonce
            )
        );
        bytes32 ethSignedMessageHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", digest));
        (uint8 v, bytes32 r, bytes32 s) = vm.sign(pk, ethSignedMessageHash);
        return abi.encodePacked(r, s, v);
    }
}

contract Target {
    bool public touched;

    function touch() external {
        touched = true;
    }
}
