"""Verify allocation vectors are mathematically correct using independent computation."""

from decimal import Decimal, ROUND_DOWN, ROUND_HALF_EVEN

import pytest

from finlint.vectors import load_suite


def allocate_equal(amount: Decimal, parts: int) -> list[Decimal]:
    """Independent implementation of largest remainder allocation.

    Uses ROUND_DOWN (floor) to compute base, then distributes remainder pennies.
    """
    base = (amount / parts).quantize(Decimal("0.01"), rounding=ROUND_DOWN)
    remainder = amount - base * parts
    pennies = int(remainder / Decimal("0.01"))
    return [
        base + Decimal("0.01") if i < pennies else base
        for i in range(parts)
    ]


def allocate_weighted(amount: Decimal, weights: list[Decimal]) -> list[Decimal]:
    """Independent implementation of weighted allocation."""
    total_weight = sum(weights)
    raw = [
        (amount * w / total_weight).quantize(Decimal("0.01"), rounding=ROUND_DOWN)
        for w in weights
    ]
    remainder = amount - sum(raw)
    pennies = int(remainder / Decimal("0.01"))
    for i in range(abs(pennies)):
        raw[i] += Decimal("0.01") if pennies > 0 else Decimal("-0.01")
    return raw


@pytest.fixture(scope="module")
def vectors():
    return load_suite("allocation")


def test_alloc_001_third_split(vectors):
    """$100.00 ÷ 3 = [33.34, 33.33, 33.33]"""
    v = vectors[0]
    result = allocate_equal(Decimal(v["inputs"]["amount"]), v["inputs"]["parts"])
    expected = [Decimal(s) for s in v["expected"]["splits"]]
    assert result == expected


def test_alloc_002_half_split(vectors):
    """$100.01 ÷ 2 = [50.01, 50.00]"""
    v = vectors[1]
    result = allocate_equal(Decimal(v["inputs"]["amount"]), v["inputs"]["parts"])
    expected = [Decimal(s) for s in v["expected"]["splits"]]
    assert result == expected


def test_alloc_003_tiny_amount(vectors):
    """$0.03 ÷ 5 = [0.01, 0.01, 0.01, 0.00, 0.00]"""
    v = vectors[2]
    result = allocate_equal(Decimal(v["inputs"]["amount"]), v["inputs"]["parts"])
    expected = [Decimal(s) for s in v["expected"]["splits"]]
    assert result == expected


def test_alloc_004_weighted_split(vectors):
    """$10,000.00 at 70%/30% = [7000.00, 3000.00]"""
    v = vectors[3]
    weights = [Decimal(w) for w in v["inputs"]["weights"]]
    result = allocate_weighted(Decimal(v["inputs"]["amount"]), weights)
    expected = [Decimal(s) for s in v["expected"]["splits"]]
    assert result == expected


def test_alloc_005_sum_invariant(vectors):
    """For all allocation vectors, sum of splits equals original amount."""
    for v in vectors:
        expected_splits = [Decimal(s) for s in v["expected"]["splits"]]
        assert sum(expected_splits) == Decimal(v["expected"]["sum"]), (
            f"Vector {v['id']}: sum {sum(expected_splits)} != {v['expected']['sum']}"
        )


def test_alloc_float_would_fail():
    """Demonstrate that float arithmetic produces wrong allocation for $100 ÷ 3."""
    # With float: 100.0 / 3 = 33.333333...
    # round(33.333..., 2) = 33.33
    # 33.33 * 3 = 99.99 — lost a penny!
    float_base = round(100.0 / 3, 2)
    assert float_base * 3 != 100.0, "Float should lose a penny in three-way split"
