from __future__ import annotations

from dataclasses import dataclass


@dataclass(frozen=True)
class CashierAllocation:
    cashier_id: int
    amount: float


def parse_allocations(payload: dict, *, total_required: float, key: str = "allocations") -> tuple[list[CashierAllocation] | None, str | None]:
    raw = payload.get(key)
    if raw is None:
        return None, None

    if not isinstance(raw, list) or len(raw) == 0:
        return None, "Allocations must be a non-empty list."

    allocations: list[CashierAllocation] = []
    seen_cashiers: set[int] = set()
    for idx, item in enumerate(raw):
        if not isinstance(item, dict):
            return None, f"Allocation #{idx + 1} must be an object."
        if "cashier_id" not in item or "amount" not in item:
            return None, f"Allocation #{idx + 1} must include cashier_id and amount."
        try:
            cashier_id = int(item["cashier_id"])
        except Exception:
            return None, f"Allocation #{idx + 1} cashier_id is invalid."
        try:
            amount = float(item["amount"])
        except Exception:
            return None, f"Allocation #{idx + 1} amount is invalid."

        if cashier_id <= 0:
            return None, f"Allocation #{idx + 1} cashier_id is invalid."
        if amount <= 0:
            return None, f"Allocation #{idx + 1} amount must be greater than 0."
        if cashier_id in seen_cashiers:
            return None, "Duplicate cashier entries are not allowed."
        seen_cashiers.add(cashier_id)
        allocations.append(CashierAllocation(cashier_id=cashier_id, amount=amount))

    total = round(sum(a.amount for a in allocations), 2)
    required = round(float(total_required or 0), 2)
    if abs(total - required) > 0.01:
        return None, f"Allocation total {total:.2f} must equal {required:.2f}."

    return allocations, None

