"""
CA Automator — Tax Advisory computation engines (the thirteen "3. Tax
Automators — Nepal Focus" tools NOT already covered by the existing biz_tax,
personal_tax, tds, salary_tds, vat, depreciation, rate_master, the
deadline/obligation_type compliance calendar, or the transfer_pricing
module).

Pure functions only (no DB access), mirrored line-for-line in
web/tax_advisory_engines.js — same discipline as ipo_engines.py /
business_engines.py. No statutory percentage (minimum tax, PE duration
tests) is ever hard-coded here — every such figure is a value the firm
itself supplies as an input, never assumed.
"""

R2 = lambda x: round(float(x or 0), 2)


def _n(v, default=0.0):
    if v is None or v == "":
        return default
    try:
        return float(v)
    except (TypeError, ValueError):
        return default


def _safe_div(a, b):
    return (a / b) if b else 0.0


def sales_vat_reconciliation(lines, return_output_vat=None):
    """Books (sales register) vs VAT return — undeclared sales lines and the total variance."""
    rows = []
    total_taxable = total_output = 0.0
    for r in lines or []:
        taxable = _n(r.get("taxable_sales"))
        output_vat = _n(r.get("output_vat"))
        declared = bool(r.get("declared_in_return"))
        total_taxable += taxable
        total_output += output_vat
        rows.append({"id": r.get("id"), "period_label": r.get("period_label", ""), "invoice_no": r.get("invoice_no", ""),
                    "party": r.get("party", ""), "taxable_sales": R2(taxable), "output_vat": R2(output_vat),
                    "declared_in_return": declared})
    undeclared = [r for r in rows if not r["declared_in_return"]]
    undeclared_vat = sum(r["output_vat"] for r in undeclared)
    out = {"rows": rows, "total_taxable_sales": R2(total_taxable), "total_output_vat": R2(total_output),
          "undeclared_count": len(undeclared), "undeclared_output_vat": R2(undeclared_vat)}
    if return_output_vat not in (None, ""):
        rov = _n(return_output_vat)
        out["return_output_vat"] = R2(rov)
        out["variance_vs_return"] = R2(total_output - rov)
    return out


def purchase_vat_reconciliation(lines, return_input_vat=None):
    """Books (purchase register) vs VAT return — ineligible input VAT and unmatched (unconfirmed-supplier-filed) purchases."""
    rows = []
    total_taxable = total_input = total_eligible_input = 0.0
    for r in lines or []:
        taxable = _n(r.get("taxable_purchase"))
        input_vat = _n(r.get("input_vat"))
        eligible = bool(r.get("eligible", True))
        matched = bool(r.get("matched_with_supplier"))
        total_taxable += taxable
        total_input += input_vat
        if eligible:
            total_eligible_input += input_vat
        rows.append({"id": r.get("id"), "period_label": r.get("period_label", ""), "invoice_no": r.get("invoice_no", ""),
                    "party": r.get("party", ""), "taxable_purchase": R2(taxable), "input_vat": R2(input_vat),
                    "eligible": eligible, "ineligibility_reason": r.get("ineligibility_reason", ""), "matched_with_supplier": matched})
    ineligible_rows = [r for r in rows if not r["eligible"]]
    unmatched_rows = [r for r in rows if not r["matched_with_supplier"]]
    out = {"rows": rows, "total_taxable_purchase": R2(total_taxable), "total_input_vat": R2(total_input),
          "total_eligible_input_vat": R2(total_eligible_input), "ineligible_count": len(ineligible_rows),
          "ineligible_input_vat": R2(sum(r["input_vat"] for r in ineligible_rows)),
          "unmatched_count": len(unmatched_rows), "unmatched_input_vat": R2(sum(r["input_vat"] for r in unmatched_rows))}
    if return_input_vat not in (None, ""):
        riv = _n(return_input_vat)
        out["return_input_vat"] = R2(riv)
        out["variance_vs_return"] = R2(total_eligible_input - riv)
    return out


def tds_reconciliation(lines):
    """Deducted vs deposited vs return-filed, per line and totalled."""
    rows = []
    for r in lines or []:
        deducted = _n(r.get("tds_deducted"))
        deposited = _n(r.get("tds_deposited"))
        variance = deposited - deducted
        rows.append({"id": r.get("id"), "fy_label": r.get("fy_label", ""), "section": r.get("section", ""),
                    "payee": r.get("payee", ""), "base_amount": R2(_n(r.get("base_amount"))),
                    "tds_deducted": R2(deducted), "tds_deposited": R2(deposited), "variance": R2(variance),
                    "return_filed": bool(r.get("return_filed")), "fully_deposited": variance >= -0.005})
    not_deposited = [r for r in rows if not r["fully_deposited"]]
    not_filed = [r for r in rows if not r["return_filed"]]
    total_deducted = sum(r["tds_deducted"] for r in rows)
    total_deposited = sum(r["tds_deposited"] for r in rows)
    return {"rows": rows, "total_deducted": R2(total_deducted), "total_deposited": R2(total_deposited),
           "total_variance": R2(total_deposited - total_deducted),
           "short_deposited_count": len(not_deposited), "not_filed_count": len(not_filed)}


def deferred_tax_provision(lines, tax_rate_pct):
    """Book value vs tax value per item; net temporary difference x tax rate = net deferred tax (asset if book<tax, liability if book>tax)."""
    rate = _n(tax_rate_pct) / 100
    rows = []
    net_diff = 0.0
    for r in lines or []:
        book = _n(r.get("book_value"))
        tax = _n(r.get("tax_value"))
        diff = book - tax
        net_diff += diff
        rows.append({"id": r.get("id"), "particular": r.get("particular", ""), "book_value": R2(book),
                    "tax_value": R2(tax), "temporary_difference": R2(diff), "deferred_tax": R2(diff * rate)})
    net_deferred_tax = net_diff * rate
    return {"rows": rows, "tax_rate_pct": _n(tax_rate_pct), "net_temporary_difference": R2(net_diff),
           "net_deferred_tax": R2(abs(net_deferred_tax)),
           "classification": ("Net deferred tax liability" if net_deferred_tax > 0
                              else ("Net deferred tax asset" if net_deferred_tax < 0 else "Nil"))}


def minimum_tax_check(computed_tax_liability, turnover, minimum_tax_pct):
    """Floor = turnover x the firm's own confirmed minimum-tax %. Never assumes a percentage of its own."""
    liability = _n(computed_tax_liability)
    turnover_v = _n(turnover)
    pct = minimum_tax_pct
    if pct in (None, ""):
        return {"computed_tax_liability": R2(liability), "turnover": R2(turnover_v), "minimum_tax_pct": None,
               "minimum_tax_amount": None, "shortfall": None, "minimum_tax_binding": None,
               "note": "No minimum-tax percentage has been entered — confirm the currently-applicable figure before relying on this check."}
    pct_v = _n(pct)
    floor = turnover_v * pct_v / 100
    shortfall = max(0.0, floor - liability)
    return {"computed_tax_liability": R2(liability), "turnover": R2(turnover_v), "minimum_tax_pct": pct_v,
           "minimum_tax_amount": R2(floor), "shortfall": R2(shortfall), "minimum_tax_binding": shortfall > 0}


def pe_risk_assessment(factors):
    """Overall PE risk = the highest severity among the risk factors actually marked present."""
    rows = []
    severity_rank = {"Low": 1, "Medium": 2, "High": 3}
    highest = 0
    present_count = 0
    for f in factors or []:
        present = bool(f.get("present"))
        level = f.get("risk_level") or "Low"
        rows.append({"id": f.get("id"), "factor": f.get("factor", ""), "present": present,
                    "details": f.get("details", ""), "risk_level": level})
        if present:
            present_count += 1
            highest = max(highest, severity_rank.get(level, 1))
    overall = {3: "High", 2: "Medium", 1: "Low", 0: "None"}[highest]
    return {"factors": rows, "present_count": present_count, "overall_risk": overall}
