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

import { V3Fixtures } from "../helpers/V3Fixtures.sol";
import {
    BaseSepoliaRehearsalAccessControllerV3
} from "../../contracts/pilot/BaseSepoliaRehearsalAccessControllerV3.sol";
import { PartnershipBondV3Safe } from "../../contracts/bonds/PartnershipBondV3Safe.sol";
import { AccountabilityBondV3Safe } from "../../contracts/bonds/AccountabilityBondV3Safe.sol";
import { BondStateV3 } from "../../contracts/bonds/BondStateV3.sol";
import { BondMathLibV3 } from "../../contracts/lib/BondMathLibV3.sol";

/// @notice C-01 regressions: Base pilot custody is typed mutual consent only.
contract MutualConsentCustodyV3Test is V3Fixtures {
    bytes32 public constant supportedPilotModuleId = keccak256("Vaultfire.BaseV3.SafeBondFactory");
    BaseSepoliaRehearsalAccessControllerV3 internal controller;
    mapping(address => bool) internal guardianPauseTarget;

    function isGuardianPauseTarget(address target) external view returns (bool) {
        return guardianPauseTarget[target];
    }

    function pilotController() external view returns (address) {
        return address(controller);
    }

    function setUp() public {
        vm.chainId(84532);
        _setUpDependencies();
        controller = new BaseSepoliaRehearsalAccessControllerV3(address(this), 10 ether, 5 ether);
        controller.setModule(address(this), true);
        controller.setCaps(10 ether, 5 ether);
        controller.setDeployEnabled(true);
    }

    function _partnershipPilot() private returns (PartnershipBondV3Safe bond, uint256 id) {
        controller.setDesignPartner(partyA, true);
        bond = new PartnershipBondV3Safe(
            owner,
            address(0),
            address(consensus),
            address(disputeRegistry),
            2 ether,
            4 ether,
            10 ether,
            address(controller)
        );
        controller.registerGuardianPauseablePosition(partyA, address(bond), 0);
        guardianPauseTarget[address(bond)] = true;
        controller.setWriteEnabled(true);
        PartnershipBondV3Safe.Terms memory terms = _partnershipTerms(901, 1 ether, 1 ether);
        terms.objectiveRuleClass = 0;
        terms.objectiveRuleSchemaVersion = 0;
        id = _createPartnership(bond, terms);
        _fundActivePartnership(bond, id, 1 ether, 1 ether);
    }

    function _accountabilityPilot() private returns (AccountabilityBondV3Safe bond, uint256 id) {
        controller.setDesignPartner(company, true);
        bond = new AccountabilityBondV3Safe(
            owner, address(consensus), address(disputeRegistry), 4 ether, 10 ether, address(controller)
        );
        controller.registerGuardianPauseablePosition(company, address(bond), 0);
        guardianPauseTarget[address(bond)] = true;
        controller.setWriteEnabled(true);
        AccountabilityBondV3Safe.Terms memory terms = _accountabilityTerms(902, 2 ether);
        id = _createAccountability(bond, terms);
        _fundActiveAccountability(bond, id, 2 ether);
    }

    function _signPartnershipSettlement(
        PartnershipBondV3Safe bond,
        PartnershipBondV3Safe.MutualSettlement memory authorization,
        uint256 key
    ) private returns (bytes memory) {
        bytes32 digest = keccak256(
            abi.encodePacked("\x19\x01", bond.DOMAIN_SEPARATOR(), bond.hashMutualSettlement(authorization))
        );
        (uint8 v, bytes32 r, bytes32 s) = vm.sign(key, digest);
        return abi.encodePacked(r, s, v);
    }

    function test_allowed_label_plus_quality_or_reputation_hash_cannot_slash_freeze_or_uphold_dispute() external {
        (AccountabilityBondV3Safe bond, uint256 id) = _accountabilityPilot();
        bytes32 qualityHash = keccak256("quality-and-reputation-score");
        (bool attestOk,) = address(bond)
            .call(abi.encodeWithSignature("attestBreach(uint256,uint256,bytes32,uint96)", id, 1 ether, qualityHash, 44));
        (bool freezeOk,) = address(bond).call(abi.encodeWithSignature("freezeSettlement(uint256,uint96)", id, 44));
        (bool challengeOk,) = address(bond).call(abi.encodeWithSignature("challengeSettlement(uint256,uint256)", id, 0));
        assertFalse(attestOk || freezeOk || challengeOk);
        assertFalse(bond.bondOf(id).upheldBreachRecorded);
        assertEq(uint256(bond.bondOf(id).state), uint256(BondStateV3.State.Active));
    }

    function test_base_pilot_rejects_legacy_objective_labels_even_before_any_evidence_is_supplied() external {
        controller.setDesignPartner(partyA, true);
        PartnershipBondV3Safe pilotBond = new PartnershipBondV3Safe(
            owner,
            address(0),
            address(consensus),
            address(disputeRegistry),
            2 ether,
            4 ether,
            10 ether,
            address(controller)
        );
        controller.registerGuardianPauseablePosition(partyA, address(pilotBond), 0);
        guardianPauseTarget[address(pilotBond)] = true;
        controller.setWriteEnabled(true);

        PartnershipBondV3Safe.Terms memory terms = _partnershipTerms(903, 1 ether, 1 ether);
        terms.objectiveRuleClass = 1;
        bytes memory partyASignature = _signPartnership(pilotBond, terms, PARTY_A_KEY);
        bytes memory partyBSignature = _signPartnership(pilotBond, terms, PARTY_B_KEY);
        vm.expectRevert(PartnershipBondV3Safe.BadTerms.selector);
        pilotBond.createBond(terms, partyASignature, partyBSignature);
    }

    function test_behavioral_hash_and_allowed_legacy_label_cannot_settle_partnership() external {
        (PartnershipBondV3Safe bond, uint256 id) = _partnershipPilot();
        bytes32 behavioralHash = keccak256("behavioral-loyalty-profile");
        assertTrue(behavioralHash != bytes32(0));
        vm.expectRevert(PartnershipBondV3Safe.PilotMutualConsentOnly.selector);
        bond.requestSettlement(id);
        vm.expectRevert(PartnershipBondV3Safe.PilotMutualConsentOnly.selector);
        bond.freezeSettlement(id, 55, BondMathLibV3.Split(5000, 5000, 0));
        vm.expectRevert(PartnershipBondV3Safe.PilotMutualConsentOnly.selector);
        bond.challengeSettlement(id, 0);
        assertEq(uint256(bond.bondOf(id).state), uint256(BondStateV3.State.Active));
    }

    function test_mutual_settlement_binds_exact_bond_chain_terms_amounts_nonce_deadline_and_both_signatures() external {
        (PartnershipBondV3Safe bond, uint256 id) = _partnershipPilot();
        PartnershipBondV3Safe.BondData memory data = bond.bondOf(id);
        PartnershipBondV3Safe.MutualSettlement memory auth = PartnershipBondV3Safe.MutualSettlement({
            bond: address(bond),
            chainId: block.chainid,
            bondId: id,
            termsHash: data.termsHash,
            partyAAmount: 1.5 ether,
            partyBAmount: 0.5 ether,
            nonce: 1,
            deadline: uint64(block.timestamp + 1 days)
        });
        bytes memory sigA = _signPartnershipSettlement(bond, auth, PARTY_A_KEY);
        bytes memory sigB = _signPartnershipSettlement(bond, auth, PARTY_B_KEY);

        PartnershipBondV3Safe.MutualSettlement memory wrongChain = auth;
        wrongChain.chainId = 8453;
        vm.expectRevert(PartnershipBondV3Safe.BadChain.selector);
        bond.settleByMutualConsent(wrongChain, sigA, sigB);
        auth.chainId = block.chainid;
        PartnershipBondV3Safe.MutualSettlement memory wrongBond = auth;
        wrongBond.bond = address(0xB0B);
        vm.expectRevert(PartnershipBondV3Safe.BadChain.selector);
        bond.settleByMutualConsent(wrongBond, sigA, sigB);
        auth.bond = address(bond);
        PartnershipBondV3Safe.MutualSettlement memory wrongAmount = auth;
        wrongAmount.partyBAmount = 0.4 ether;
        vm.expectRevert(PartnershipBondV3Safe.InvalidMutualSettlement.selector);
        bond.settleByMutualConsent(wrongAmount, sigA, sigB);
        auth.partyBAmount = 0.5 ether;
        PartnershipBondV3Safe.MutualSettlement memory wrongTerms = auth;
        wrongTerms.termsHash = keccak256("unrelated-terms");
        vm.expectRevert(PartnershipBondV3Safe.InvalidMutualSettlement.selector);
        bond.settleByMutualConsent(wrongTerms, sigA, sigB);
        auth.termsHash = data.termsHash;
        vm.expectRevert();
        bond.settleByMutualConsent(auth, sigA, "");

        vm.prank(owner);
        bond.pause();
        vm.expectRevert(PartnershipBondV3Safe.Paused.selector);
        bond.settleByMutualConsent(auth, sigA, sigB);
        vm.prank(owner);
        bond.unpause();
        bond.settleByMutualConsent(auth, sigA, sigB);
        assertEq(uint256(bond.bondOf(id).state), uint256(BondStateV3.State.Settled));
        assertEq(bond.creditOf(address(0), partyA), 1.5 ether);
        assertEq(bond.creditOf(address(0), partyB), 0.5 ether);
        vm.expectRevert(PartnershipBondV3Safe.AuthorizationUsed.selector);
        bond.settleByMutualConsent(auth, sigA, sigB);
    }

    function test_accountability_expiry_principal_recovery_remains_bounded() external {
        (AccountabilityBondV3Safe bond, uint256 id) = _accountabilityPilot();
        AccountabilityBondV3Safe.BondData memory data = bond.bondOf(id);
        vm.warp(data.terms.activePeriodEnd);
        bond.markExpired(id);
        vm.prank(address(0xBEEF));
        bond.forceExit(id);
        assertEq(bond.creditOf(address(0), company), 2 ether);
    }
}
