"""
Company identity and country resolution (final master pass, §18).

## Why this exists

§18 requires four *separate* country facts about a company, each with its own provenance:

    Company Country · Exchange Country · Incorporation Country · Headquarters Country

and states plainly: **"НЕ извеждай Company Country само от exchange"** — do not derive the
company's country from where it happens to be listed. The canonical example in the specification
is a real and common structure:

    Company Country = China · Incorporation Country = Cayman Islands · Exchange Country = USA

Before this module, `Company` had exactly one `country_id`, populated from whatever single country
string the provider returned, with no record of which of the four it was.

Worse, `get_or_create_company()` created the **Exchange** row with
`country_id = <the company's country>`. Exchanges are shared across every company listed on them,
so the first company ingested set NYSE's country for everyone afterwards — one Chinese ADR
ingested first and NYSE is recorded as being in China. That is a shared-reference-table
corruption, not just a missing distinction.

## What this module does, and what it refuses to do

It resolves what can be resolved from signals the platform actually has, and marks everything else
`UNKNOWN` with a reason. It never guesses.

- **Exchange country** comes from a static MIC→country table below, which is a fact about the
  venue and has nothing to do with any company listed on it.
- **Company country** comes from the provider when the provider supplies one. When it does not,
  the answer is `UNKNOWN` — explicitly **not** the exchange's country, because that is the exact
  inference §18 forbids. The provenance field records which happened.
- **Incorporation country** and **headquarters country** are `NOT_INGESTED`: no adapter in this
  repository fetches either. FMP's company profile returns one `country` field whose meaning is
  not documented as being either of them, and EODHD's returns `CountryISO`. Mapping one provider
  field to three different questions and presenting the answer three times would be fabrication.

Pure and dependency-free (stdlib only), so it is genuinely testable here; the DB wiring that uses
it lives in `app/workers/ingest.py`.
"""
from __future__ import annotations

from dataclasses import dataclass
from typing import Optional

from app.engines.reference_data import canonical_mic, exchange_country_map

#: Provenance values, ordered from strongest to weakest.
PROVENANCE_PROVIDER = "PROVIDER"            # the data provider stated it
PROVENANCE_EXCHANGE_MIC = "EXCHANGE_MIC"    # derived from the listing venue (venue facts only)
PROVENANCE_UNKNOWN = "UNKNOWN"              # no signal available
PROVENANCE_NOT_INGESTED = "NOT_INGESTED"    # nothing in this repo fetches this fact at all

#: Every exchange identifier this platform recognises → the ISO 3166-1 alpha-2 country of the
#: *venue*. Derived from `app/engines/reference_data.py`, which is also what
#: `seed_reference_data()` writes into the `exchanges` table — one source, so the resolver and the
#: seeded rows cannot disagree. They previously could, and did: the demo universe carried `XSOF`
#: for the Bulgarian Stock Exchange while this table knew only the real MIC, `XBUL`, so that one
#: company could never resolve a venue country and ingestion died on a NOT NULL constraint.
#:
#: Includes canonical MICs, the provider short names FMP returns instead of MICs ("NASDAQ"), and
#: the provider-only codes that identify a country but no single ISO-assigned venue ("OTC").
#: An unknown code resolves to UNKNOWN rather than to a guess.
EXCHANGE_COUNTRY_BY_MIC: dict[str, str] = exchange_country_map()


@dataclass(frozen=True)
class CountryFact:
    """One country answer plus how it was arrived at. `iso2` is None when unknown."""

    iso2: Optional[str]
    provenance: str
    reason: Optional[str] = None

    @property
    def known(self) -> bool:
        return self.iso2 is not None


@dataclass(frozen=True)
class CompanyIdentity:
    """The four §18 country facts, each independently sourced."""

    company_country: CountryFact
    exchange_country: CountryFact
    incorporation_country: CountryFact
    headquarters_country: CountryFact

    def as_dict(self) -> dict:
        return {
            name: {"iso2": fact.iso2, "provenance": fact.provenance, "reason": fact.reason}
            for name, fact in (
                ("company_country", self.company_country),
                ("exchange_country", self.exchange_country),
                ("incorporation_country", self.incorporation_country),
                ("headquarters_country", self.headquarters_country),
            )
        }

    @property
    def is_cross_border_listing(self) -> Optional[bool]:
        """True when the company's country and its listing venue's country differ — the ADR /
        foreign-listing case §18 calls out. None when either side is unknown, because "we do not
        know" is not the same as "they match"."""
        if not (self.company_country.known and self.exchange_country.known):
            return None
        return self.company_country.iso2 != self.exchange_country.iso2


def exchange_country_for_mic(exchange_mic: Optional[str]) -> CountryFact:
    """Country of the listing venue. A fact about the venue, never about a company on it."""
    if not exchange_mic:
        return CountryFact(None, PROVENANCE_UNKNOWN, "no exchange identifier supplied")
    iso2 = EXCHANGE_COUNTRY_BY_MIC.get(canonical_mic(exchange_mic))
    if iso2 is None:
        return CountryFact(
            None, PROVENANCE_UNKNOWN,
            f"MIC/exchange code {exchange_mic!r} is not in EXCHANGE_COUNTRY_BY_MIC — add it "
            f"explicitly rather than inferring from the ticker suffix",
        )
    return CountryFact(iso2, PROVENANCE_EXCHANGE_MIC)


def resolve_company_identity(
    provider_country_iso2: Optional[str],
    exchange_mic: Optional[str],
) -> CompanyIdentity:
    """Resolve the four country facts from the signals this platform actually has.

    `provider_country_iso2` is the single country string a provider profile returns. It is treated
    as the **company's** country — which is what both FMP and EODHD document it as — and is NOT
    reused for incorporation or headquarters, which are different questions with different answers
    for exactly the structures §18 is about.
    """
    if provider_country_iso2:
        company = CountryFact(provider_country_iso2.strip().upper(), PROVENANCE_PROVIDER)
    else:
        company = CountryFact(
            None, PROVENANCE_UNKNOWN,
            "the provider profile carried no country; deliberately NOT defaulted to the exchange's "
            "country — a US-listed company is not thereby a US company (§18)",
        )

    return CompanyIdentity(
        company_country=company,
        exchange_country=exchange_country_for_mic(exchange_mic),
        incorporation_country=CountryFact(
            None, PROVENANCE_NOT_INGESTED,
            "no adapter in this repository fetches an incorporation country; it is not the same "
            "field as the provider's `country` and must not be copied from it",
        ),
        headquarters_country=CountryFact(
            None, PROVENANCE_NOT_INGESTED,
            "no adapter in this repository fetches a headquarters country separately from the "
            "provider's single `country` field",
        ),
    )
