# Copyright (c) 2026 Mohammed Mahraj ul Alam Samrat,
# Head of Trade Facilitation & Regulatory Compliance, TRACE Consulting Ltd.
# All rights reserved. BETA release.
# See LICENSE in the repository root.
"""Duty calculation engine — server-side port of the platform's calculator.

Legal structure (all bases configurable; verify against current law):
  invoice (qty x unit USD)  ->  + freight + insurance (+ notional %) + additions - deductions
  = CIF USD  ->  x FX = CIF BDT  ->  + landing charge % (default 1%, Valuation Rules
  2000 practice, TO BE VERIFIED under Customs Act 2023 rules)  ->  Assessable Value.
  CD & RD on AV (statutory); SD default on AV+CD+RD; VAT & AT default on
  AV+CD+RD+SD; AIT default on AV (VAT & SD Act 2012; Income Tax Act 2023).
Decision-support only — not a binding assessment, ruling or legal opinion.
"""
from dataclasses import dataclass, field
from typing import Optional

BASES = {"av", "avcd", "avcdrd", "avcdrdsd"}
DEFAULT_CFG = {"SD": "avcdrd", "VAT": "avcdrdsd", "AT": "avcdrdsd", "AIT": "av"}


@dataclass
class CalcInput:
    qty: float
    unit_value_usd: float
    fx: float                                  # BDT per USD (NBR-notified rate)
    basis: str = "FOB"                         # FOB | CFR | CIF | EXW | OTH
    freight_usd: float = 0.0
    insurance_usd: float = 0.0
    notional_insurance_pct: Optional[float] = None   # e.g. 1.0 if actual unknown
    additions_usd: float = 0.0
    deductions_usd: float = 0.0
    landing_pct: float = 1.0                   # 0 to disable
    av_override_bdt: Optional[float] = None
    av_override_reason: str = ""
    bases: dict = field(default_factory=lambda: dict(DEFAULT_CFG))


def _base(code: str, av: float, cd_amt: float, rd_amt: float, sd_amt: float) -> float:
    return {"av": av, "avcd": av + cd_amt, "avcdrd": av + cd_amt + rd_amt,
            "avcdrdsd": av + cd_amt + rd_amt + sd_amt}[code]


def compute(inp: CalcInput, rates: dict) -> dict:
    """rates: {'cd','rd','sd','vat','ait','at'} in percent for the chosen FY."""
    warnings = []
    for f in ("qty", "unit_value_usd", "fx", "freight_usd", "insurance_usd",
              "additions_usd", "deductions_usd", "landing_pct"):
        if getattr(inp, f) is not None and getattr(inp, f) < 0:
            raise ValueError(f"negative value rejected: {f}")
    if inp.fx <= 0:
        raise ValueError("exchange rate missing")
    for k, v in inp.bases.items():
        if v not in BASES:
            raise ValueError(f"unknown base '{v}' for {k}")

    invoice = inp.qty * inp.unit_value_usd
    ins = inp.insurance_usd
    ins_note = None
    if not ins and inp.notional_insurance_pct and inp.basis != "CIF":
        ins = (invoice + inp.freight_usd) * inp.notional_insurance_pct / 100.0
        ins_note = f"notional {inp.notional_insurance_pct}% of C&F — to be verified"
    cif_usd = invoice + inp.freight_usd + ins + inp.additions_usd - inp.deductions_usd
    cif_bdt = cif_usd * inp.fx
    landing = cif_bdt * inp.landing_pct / 100.0
    av = cif_bdt + landing
    overridden = False
    if inp.av_override_bdt and inp.av_override_bdt > 0:
        if not inp.av_override_reason.strip():
            warnings.append("AV overridden without explanation — record the valuation method (WTO CVA 2-6)")
        av, overridden = inp.av_override_bdt, True
    if av < invoice * inp.fx and not overridden and not inp.deductions_usd:
        warnings.append("assessable value below invoice value without explanation — undervaluation query risk")
    if inp.basis != "CIF" and not inp.freight_usd:
        warnings.append("no freight entered on a non-CIF basis — customs value may be understated")

    cd_amt = av * rates.get("cd", 0) / 100
    rd_amt = av * rates.get("rd", 0) / 100
    sd_b = _base(inp.bases["SD"], av, cd_amt, rd_amt, 0)
    sd_amt = sd_b * rates.get("sd", 0) / 100
    vat_b = _base(inp.bases["VAT"], av, cd_amt, rd_amt, sd_amt)
    vat_amt = vat_b * rates.get("vat", 0) / 100
    at_b = _base(inp.bases["AT"], av, cd_amt, rd_amt, sd_amt)
    at_amt = at_b * rates.get("at", 0) / 100
    ait_b = _base(inp.bases["AIT"], av, cd_amt, rd_amt, sd_amt)
    ait_amt = ait_b * rates.get("ait", 0) / 100

    total = cd_amt + rd_amt + sd_amt + vat_amt + at_amt + ait_amt
    landed = av + total
    return {
        "invoice_usd": round(invoice, 2), "cif_usd": round(cif_usd, 2),
        "cif_bdt": round(cif_bdt, 2), "landing_bdt": round(landing, 2),
        "assessable_value_bdt": round(av, 2), "av_overridden": overridden,
        "insurance_note": ins_note,
        "components": {
            "CD":  {"rate": rates.get("cd", 0),  "base": "av",             "base_amt": round(av, 2),    "amount": round(cd_amt, 2)},
            "RD":  {"rate": rates.get("rd", 0),  "base": "av",             "base_amt": round(av, 2),    "amount": round(rd_amt, 2)},
            "SD":  {"rate": rates.get("sd", 0),  "base": inp.bases["SD"],  "base_amt": round(sd_b, 2),  "amount": round(sd_amt, 2)},
            "VAT": {"rate": rates.get("vat", 0), "base": inp.bases["VAT"], "base_amt": round(vat_b, 2), "amount": round(vat_amt, 2)},
            "AT":  {"rate": rates.get("at", 0),  "base": inp.bases["AT"],  "base_amt": round(at_b, 2),  "amount": round(at_amt, 2)},
            "AIT": {"rate": rates.get("ait", 0), "base": inp.bases["AIT"], "base_amt": round(ait_b, 2), "amount": round(ait_amt, 2)},
        },
        "total_duties_taxes_bdt": round(total, 2),
        "landed_cost_bdt": round(landed, 2),
        "effective_incidence_pct": round(total / av * 100, 2) if av else None,
        "cost_per_unit_bdt": round(landed / inp.qty, 2) if inp.qty else None,
        "warnings": warnings,
        "disclaimer": ("Decision-support only — not a binding customs assessment, advance ruling, "
                       "valuation ruling, tariff ruling or legal opinion. Verify against the latest "
                       "Bangladesh Customs Tariff, Customs Act, VAT & SD Act 2012, Income Tax Act 2023, "
                       "Finance Act, NBR SROs, Ministry of Commerce IPO and official assessment."),
    }
