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

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

/// @notice Regression coverage for full-width settlement arithmetic. A
/// deployment cap may legally be close to uint256.max, so a percentage
/// computation must not depend on a non-overflowing intermediate product.
contract BondMathOverflowHarness {
    function split(uint256 principal, uint256 humanBps, uint256 aiBps, uint256 partnershipBps)
        external
        pure
        returns (uint256 human, uint256 ai, uint256 partnership)
    {
        return BondMathLibV3.splitPrincipal(
            principal,
            BondMathLibV3.Split({ humanShareBps: humanBps, aiShareBps: aiBps, partnershipShareBps: partnershipBps })
        );
    }

    function multiply(uint256 base, uint256 multiplierBps) external pure returns (uint256) {
        return BondMathLibV3.applyMultiplier(base, multiplierBps);
    }
}

contract BondMathOverflowV3Test is Test {
    BondMathOverflowHarness private harness;

    function setUp() public {
        harness = new BondMathOverflowHarness();
    }

    function test_maxPrincipalZeroPartnershipSplitAssignsRemainderToPartyB() external view {
        uint256 principal = type(uint256).max;
        (uint256 human, uint256 ai, uint256 partnership) = harness.split(principal, 5_000, 5_000, 0);

        assertEq(human, principal / 2, "human floor rounding changed");
        assertEq(ai, (principal / 2) + 1, "party B did not receive rounding remainder");
        assertEq(partnership, 0, "zero partnership share received principal");
        assertEq(human + ai + partnership, principal, "split no longer conserves principal");
    }

    function test_maxBaseMultiplierDoesNotOverflowIntermediateProduct() external view {
        assertEq(harness.multiply(type(uint256).max, 10_000), type(uint256).max);
    }

    function testFuzz_splitConservesPrincipalAndRoundsTowardZero(
        uint128 rawPrincipal,
        uint16 rawHumanBps,
        uint16 rawAiBps
    ) external view {
        uint256 principal = uint256(rawPrincipal);
        uint256 humanBps = bound(uint256(rawHumanBps), 0, 10_000);
        uint256 aiBps = bound(uint256(rawAiBps), 0, 10_000 - humanBps);
        uint256 partnershipBps = 10_000 - humanBps - aiBps;

        (uint256 human, uint256 ai, uint256 partnership) = harness.split(principal, humanBps, aiBps, partnershipBps);

        assertEq(human, (principal * humanBps) / 10_000, "human rounding");
        if (partnershipBps == 0) {
            assertEq(ai, principal - human, "party B receives zero-share remainder");
        } else {
            assertEq(ai, (principal * aiBps) / 10_000, "AI rounding");
        }
        assertEq(human + ai + partnership, principal, "principal conservation");
    }

    function testFuzz_zeroPartnershipShareConservesPrincipalWithoutOwnerRemainder(
        uint128 rawPrincipal,
        uint16 rawHumanBps
    ) external view {
        uint256 principal = uint256(rawPrincipal);
        uint256 humanBps = bound(uint256(rawHumanBps), 0, 10_000);
        uint256 aiBps = 10_000 - humanBps;

        (uint256 human, uint256 ai, uint256 partnership) = harness.split(principal, humanBps, aiBps, 0);

        assertEq(human, (principal * humanBps) / 10_000, "human rounding");
        assertEq(partnership, 0, "zero partnership share received principal");
        assertEq(human + ai, principal, "participant principal conservation");
    }
}
