"""Localization registry — languages, locales, calendars, currencies.

Architecture:
  - ``Country`` is the primary legal/financial base: determines currency,
    calendar system, RTL/LTR default, weekend days, and timezone defaults.
  - ``Language`` is the display base: determines translations, labels, numeral
    system, and pluralization rules.
  - ``Locale`` = Language + Country/Region; stores all formatting overrides.
  - ``LocalizationProfile`` is an abstract mixin for the inheritance chain:
    Platform → Tenant → Organization → Workspace → User.

The seed ships ``en``, ``fa`` (Persian), and ``ar`` (Arabic) so the platform
is i18n-ready out of the box, but additional rows can be added without code
changes.
"""

from __future__ import annotations

from typing import ClassVar

from django.db import models
from django.utils.translation import gettext_lazy as _

from simorgh.core.models import TimeStampedModel


class TextDirection(models.TextChoices):
    LTR = "ltr", _("Left-to-right")
    RTL = "rtl", _("Right-to-left")


class CalendarSystem(models.TextChoices):
    GREGORIAN = "gregorian", _("Gregorian")
    JALALI = "jalali", _("Jalali (Solar Hijri)")
    HIJRI = "hijri", _("Hijri (Lunar)")
    FISCAL = "fiscal", _("Fiscal calendar")


class NumeralSystem(models.TextChoices):
    LATIN = "latin", _("Latin (0-9)")
    EASTERN_ARABIC = "eastern_arabic", _("Eastern Arabic (٠-٩)")
    PERSIAN = "persian", _("Persian (۰-۹)")


class Country(TimeStampedModel):
    """Country reference data — the legal/financial localization base.

    Determines default currency, calendar, direction (RTL/LTR), timezone,
    first day of week, and weekend days. All of these can be overridden at
    lower levels of the hierarchy (Tenant → Org → Workspace → User).

    The ``flag_emoji`` is auto-derivable from ``iso2`` but stored explicitly so
    templates and APIs can render it without Python logic.
    """

    iso2 = models.CharField(_("ISO 2-letter code"), max_length=2, unique=True)
    iso3 = models.CharField(_("ISO 3-letter code"), max_length=3, blank=True, default="")
    name_english = models.CharField(_("English name"), max_length=64)
    name_native = models.CharField(_("native name"), max_length=64, blank=True, default="")
    dial_code = models.CharField(_("dial code"), max_length=8, help_text=_("E.164 prefix, e.g. +98"))
    flag_emoji = models.CharField(_("flag emoji"), max_length=10, blank=True, default="")
    is_enabled = models.BooleanField(_("enabled"), default=True, db_index=True)
    sort_order = models.PositiveIntegerField(_("sort order"), default=100)

    # --- Localization defaults (all overrideable at lower levels) ---
    default_currency = models.CharField(
        _("default currency"), max_length=8, default="USD",
        help_text=_("ISO 4217 currency code, e.g. IRR, USD, EUR"),
    )
    default_calendar = models.CharField(
        _("default calendar"),
        max_length=16,
        choices=CalendarSystem.choices,
        default=CalendarSystem.GREGORIAN,
        help_text=_("Calendar system used in this country (e.g. IR→jalali, SA→hijri)"),
    )
    default_direction = models.CharField(
        _("default direction"),
        max_length=3,
        choices=TextDirection.choices,
        default=TextDirection.LTR,
        help_text=_("RTL/LTR default for this country's primary language"),
    )
    default_timezone = models.CharField(
        _("default timezone"), max_length=64, default="UTC",
        help_text=_("IANA timezone name, e.g. Asia/Tehran"),
    )
    first_day_of_week = models.PositiveSmallIntegerField(
        _("first day of week"), default=0,
        help_text=_("0 = Monday … 6 = Sunday"),
    )
    weekend_days = models.CharField(
        _("weekend days"), max_length=20, default="5,6",
        help_text=_("Comma-separated 0-6 values (0=Mon). E.g. '5,6'=Sat,Sun; '4,5'=Thu,Fri"),
    )

    class Meta:
        verbose_name = _("Country")
        verbose_name_plural = _("Countries")
        ordering: ClassVar[list[str]] = ["sort_order", "name_english"]

    def __str__(self) -> str:
        return f"{self.flag_emoji} {self.name_english} ({self.dial_code})"

    def save(self, *args: object, **kwargs: object) -> None:
        """Auto-compute flag_emoji from iso2 if not explicitly set."""
        if not self.flag_emoji and len(self.iso2) == 2:
            a = chr(0x1F1E6 + ord(self.iso2[0].upper()) - ord("A"))
            b = chr(0x1F1E6 + ord(self.iso2[1].upper()) - ord("A"))
            self.flag_emoji = a + b
        super().save(*args, **kwargs)


class Language(TimeStampedModel):
    """A language available in the platform UI / admin / API responses.

    Language determines translations, labels, date-display language, numeral
    system, and pluralization. Calendar and weekend rules live on ``Country``,
    not here — a ``fa`` speaker in UAE uses Gregorian, not Jalali.
    """

    code = models.CharField(_("code"), max_length=10, unique=True)
    name_native = models.CharField(_("native name"), max_length=64)
    name_english = models.CharField(_("English name"), max_length=64)
    direction = models.CharField(
        _("direction"), max_length=3, choices=TextDirection.choices, default=TextDirection.LTR,
        help_text=_("Default text direction for this language (overrideable at Locale/User level)"),
    )
    numeral_system = models.CharField(
        _("numeral system"),
        max_length=20,
        choices=NumeralSystem.choices,
        default=NumeralSystem.LATIN,
        help_text=_("Default numeral system used when writing in this language"),
    )
    is_enabled = models.BooleanField(_("enabled"), default=True, db_index=True)
    is_default = models.BooleanField(_("default"), default=False)
    sort_order = models.PositiveIntegerField(_("sort order"), default=100)

    class Meta:
        verbose_name = _("Language")
        verbose_name_plural = _("Languages")
        ordering: ClassVar[list[str]] = ["sort_order", "code"]

    def __str__(self) -> str:
        return f"{self.name_english} ({self.code})"


class Locale(TimeStampedModel):
    """BCP 47-style locale = Language + Country/Region.

    Stores all formatting overrides for a specific language-region combination
    (e.g. ``fa-IR``, ``en-US``, ``ar-SA``, ``en-AE``).  Empty override fields
    mean "inherit from country or language defaults".

    Stores formatting *patterns* — not behavior — so a centralized formatter
    layer (frontend + backend) can apply them uniformly.
    """

    code = models.CharField(_("code"), max_length=16, unique=True,
        help_text=_("BCP 47 tag, e.g. fa-IR, en-US, ar-SA"))
    language = models.ForeignKey(
        Language, on_delete=models.PROTECT, related_name="locales", verbose_name=_("language")
    )
    country = models.ForeignKey(
        Country, on_delete=models.SET_NULL, null=True, blank=True,
        related_name="locales", verbose_name=_("country"),
        help_text=_("Country this locale is specific to (null = language-only locale)"),
    )
    # --- Formatting patterns (empty = inherit from country/language defaults) ---
    calendar = models.CharField(
        _("calendar"), max_length=16, choices=CalendarSystem.choices, blank=True, default="",
        help_text=_("Override calendar; blank = inherit from country.default_calendar"),
    )
    direction = models.CharField(
        _("direction"), max_length=3, choices=TextDirection.choices, blank=True, default="",
        help_text=_("Override direction; blank = inherit from language.direction"),
    )
    numeral_system = models.CharField(
        _("numeral system"), max_length=20, choices=NumeralSystem.choices, blank=True, default="",
        help_text=_("Override numeral system; blank = inherit from language.numeral_system"),
    )
    date_format = models.CharField(_("date format"), max_length=32, default="YYYY-MM-DD")
    time_format = models.CharField(_("time format"), max_length=32, default="HH:mm")
    datetime_format = models.CharField(
        _("datetime format"), max_length=64, default="YYYY-MM-DD HH:mm"
    )
    first_day_of_week = models.PositiveSmallIntegerField(
        _("first day of week"), default=0, help_text=_("0 = Monday, 6 = Sunday")
    )
    number_decimal_separator = models.CharField(_("decimal separator"), max_length=2, default=".")
    number_thousand_separator = models.CharField(_("thousand separator"), max_length=2, default=",")
    currency_code = models.CharField(_("default currency"), max_length=8, default="USD")
    timezone = models.CharField(_("timezone"), max_length=64, default="UTC")
    is_default = models.BooleanField(_("default locale"), default=False)

    class Meta:
        verbose_name = _("Locale")
        verbose_name_plural = _("Locales")
        ordering: ClassVar[list[str]] = ["code"]

    def __str__(self) -> str:
        return self.code

    @property
    def effective_calendar(self) -> str:
        """Return the active calendar: locale override → country default → gregorian."""
        if self.calendar:
            return self.calendar
        if self.country_id and self.country.default_calendar:
            return self.country.default_calendar
        return CalendarSystem.GREGORIAN

    @property
    def effective_direction(self) -> str:
        """Return the active direction: locale override → language default → ltr."""
        if self.direction:
            return self.direction
        if self.language_id:
            return self.language.direction
        return TextDirection.LTR

    @property
    def effective_numeral_system(self) -> str:
        """Return the active numeral system: locale override → language default → latin."""
        if self.numeral_system:
            return self.numeral_system
        if self.language_id:
            return self.language.numeral_system
        return NumeralSystem.LATIN


class Currency(TimeStampedModel):
    code = models.CharField(_("code"), max_length=8, unique=True)
    name = models.CharField(_("name"), max_length=64)
    symbol = models.CharField(_("symbol"), max_length=8)
    decimals = models.PositiveSmallIntegerField(_("decimals"), default=2)
    is_enabled = models.BooleanField(_("enabled"), default=True, db_index=True)

    class Meta:
        verbose_name = _("Currency")
        verbose_name_plural = _("Currencies")
        ordering: ClassVar[list[str]] = ["code"]

    def __str__(self) -> str:
        return f"{self.code} ({self.symbol})"


class Timezone(TimeStampedModel):
    """IANA timezone reference for user preferences."""

    name = models.CharField(_("IANA name"), max_length=64, unique=True)
    display_name = models.CharField(_("display name"), max_length=100, blank=True, default="")
    utc_offset = models.CharField(_("UTC offset"), max_length=10, blank=True, default="")
    is_enabled = models.BooleanField(_("enabled"), default=True, db_index=True)
    sort_order = models.PositiveIntegerField(_("sort order"), default=100)

    class Meta:
        verbose_name = _("Timezone")
        verbose_name_plural = _("Timezones")
        ordering: ClassVar[list[str]] = ["sort_order", "name"]

    def __str__(self) -> str:
        if self.utc_offset:
            return f"{self.name} ({self.utc_offset})"
        return self.name


class LocalizationProfile(models.Model):
    """Abstract mixin that adds localization overrides to any model.

    Include this in Tenant, Organization, Workspace, or User to participate
    in the inheritance chain:
        Platform defaults → Tenant → Org → Workspace → User preferences

    Each level can override; resolving the effective locale walks up the chain
    until a non-null / non-empty value is found.

    Usage::

        class MyTenant(LocalizationProfile, TimeStampedModel):
            ...
    """

    locale = models.ForeignKey(
        Locale,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="+",
        verbose_name=_("locale"),
        help_text=_("Preferred locale (language + region formatting)"),
    )
    timezone_override = models.CharField(
        _("timezone override"), max_length=64, blank=True, default="",
        help_text=_("IANA timezone; blank = inherit from locale or country default"),
    )
    calendar_override = models.CharField(
        _("calendar override"),
        max_length=16,
        choices=CalendarSystem.choices,
        blank=True,
        default="",
        help_text=_("Calendar override; blank = inherit from locale or country default"),
    )
    direction_override = models.CharField(
        _("direction override"),
        max_length=3,
        choices=TextDirection.choices,
        blank=True,
        default="",
        help_text=_("Direction override; blank = inherit from locale or language default"),
    )

    class Meta:
        abstract = True


__all__ = [
    "CalendarSystem",
    "Country",
    "Currency",
    "Language",
    "Locale",
    "LocalizationProfile",
    "NumeralSystem",
    "TextDirection",
    "Timezone",
]
