"""
Country Service — the single place code asks "which country, which rules?"

This is the seed of the Country Engine: today it does two small, safe things
— resolve a firm's configured country and look it up in the existing
tp_country registry (already populated with NP/IN/AE plus every ISO-3166
country as location-only reference data, see seed.sql) — so that nothing
in engines.py, app.py or anywhere else needs an `if country_code == 'NP'`
of its own. Every call site should go through here rather than reading
firm.country_code or rate_master directly.

As country-aware rate/rule data grows (rate_master rows for a second
country, or a generalisation of rate_master into tp_regulatory_rule),
this module is where that lookup logic gets added — the call sites below
do not change shape.
"""
from contextlib import contextmanager

import db

DEFAULT_COUNTRY = "NP"

# Which country(ies) a module_key genuinely applies to. A module NOT listed
# here applies to every country — deliberately the common case: most of
# this app's modules (Transfer Pricing, Practice Management, Forensic
# Audit, Loan/EMI, Lease, Valuation, FDI, Inventory, Fin Statements, Grant,
# Projection, DD, Capital Advisory, Fixed Assets, Audit Execution,
# Corporate/Business/IPO/Tax Advisory, Accounting Automation, Statutory/
# Internal Audit...) have no hard-coded statutory numbers at all (confirmed
# by grepping for rate_dict()/rates() calls across every engine file, not
# assumed) — see PROJECT_STATE.md's UAE module audit. Only list a module
# here when it genuinely doesn't apply everywhere, and say why.
MODULE_COUNTRY_APPLICABILITY = {
    # UAE has no personal income tax and no salary-TDS/general withholding
    # tax regime at all — these three don't just need different rates, the
    # underlying tax doesn't exist there.
    "personal_tax": {"NP", "IN"},
    "salary_tds": {"NP", "IN"},
    "tds": {"NP", "IN"},
    # Nepal Schedule 2 pools (absorption tranches by Bikram Sambat month) —
    # a different mechanic per country, see engines.depreciation()'s
    # docstring and india_engines.india_depreciation()'s. UAE's Corporate
    # Tax generally accepts accounting (IFRS) depreciation as-is rather
    # than a separate statutory schedule, so there is no UAE counterpart
    # module for this at all yet — not an oversight, a genuinely different
    # shape of problem (or arguably not needed as a distinct tax module).
    "depreciation": {"NP"},
    "india_depreciation": {"IN"},
    # Nepal's flat company_rate_<category> lookup can't represent UAE's
    # tiered 0%-then-9% Corporate Tax — see uae_engines.py.
    "biz_tax": {"NP"},
    "uae_corporate_tax": {"AE"},
    # Nepal's Sec 117-120 instalment/interest structure and India's Sec
    # 234A/B/C/F are both genuinely different shapes from UAE's mostly
    # fixed-AED administrative penalties — see uae_engines.py.
    "penalty": {"NP"},
    "india_gst": {"IN"},
    "india_interest_penalty": {"IN"},
    "uae_penalties": {"AE"},
}


def is_module_applicable(module_key, country_code):
    limited_to = MODULE_COUNTRY_APPLICABILITY.get(module_key)
    return limited_to is None or country_code in limited_to


def firm_country_code(firm_id):
    row = db.query("SELECT country_code FROM firm WHERE id = ?", (firm_id,), one=True)
    return (row["country_code"] if row and row["country_code"] else DEFAULT_COUNTRY)


def client_country_code(client_id, firm_id):
    """A client's own jurisdiction if set (client.country_code — a firm's
    clients aren't all necessarily in the firm's own home country), else
    the firm's own. Every workfile is scoped to a client, so this is the
    right granularity for engine computation — see use_country_for_client."""
    row = db.query("SELECT country_code FROM client WHERE id = ? AND firm_id = ?", (client_id, firm_id), one=True)
    if row and row["country_code"]:
        return row["country_code"]
    return firm_country_code(firm_id)


def country(code):
    """One row from the tp_country registry, or None for an unrecognised code."""
    return db.query("SELECT * FROM tp_country WHERE code = ?", (code,), one=True)


def active_countries():
    """Countries with real regulatory content configured (tax_authority set) —
    i.e. the set a firm could plausibly select as its own jurisdiction, as
    opposed to the full ISO-3166 list usable only as a related-party location."""
    return db.query("SELECT * FROM tp_country WHERE is_active = 1 AND tax_authority IS NOT NULL ORDER BY name")


def rates(firm_id, domain=None, fy=None, applies_to=None):
    """Country-aware equivalent of db.rates() — resolves the firm's own
    jurisdiction instead of assuming Nepal."""
    return db.rates(domain, fy, applies_to, country_code=firm_country_code(firm_id))


def rate_dict(firm_id, domain, fy, applies_to=None):
    """Country-aware equivalent of db.rate_dict() — resolves the firm's own
    jurisdiction instead of assuming Nepal."""
    return db.rate_dict(domain, fy, applies_to, country_code=firm_country_code(firm_id))


def current_country_code():
    """The country set by the innermost use_country/use_firm_country block
    currently in effect, or 'NP' outside any such block."""
    return db.get_current_country()


@contextmanager
def use_country(country_code):
    """Makes every db.rates()/db.rate_dict() call made anywhere underneath
    this block — including deep inside engines.py, which calls them by
    domain only and takes no country_code parameter of its own — resolve
    against country_code instead of the 'NP' default. This is what actually
    makes the six core engines (and every other rate_master-driven engine)
    country-aware: wrap the request, not each of their ~15 call sites."""
    token = db.set_current_country(country_code)
    try:
        yield
    finally:
        db.reset_current_country(token)


@contextmanager
def use_firm_country(firm_id):
    """use_country, resolved from the firm's own configured jurisdiction."""
    with use_country(firm_country_code(firm_id)):
        yield


@contextmanager
def use_country_for_client(firm_id, client_id):
    """use_country, resolved from a specific client's own jurisdiction when
    set, else the firm's — the right choice for anything computing a
    client's workfile. client_id may be None (a standalone computation with
    no client yet), in which case this is exactly use_firm_country."""
    code = client_country_code(client_id, firm_id) if client_id else firm_country_code(firm_id)
    with use_country(code):
        yield
