"""Locale-aware Faker support for generating realistic multilingual data.

Supports fa_IR (Persian), ar_SA (Arabic), en_US (English) locales.
Provides helpers for Persian/Arabic-specific data types (Shamsi dates, national IDs, etc.).
"""

from __future__ import annotations

import random
from typing import TYPE_CHECKING, Any

if TYPE_CHECKING:
    from collections.abc import Iterator


class ScopedFaker:
    """Locale-aware Faker wrapper with multi-locale support.

    Usage:
        faker = ScopedFaker(locales=["fa_IR", "en_US"])
        name = faker.name()
        persian_date = faker.jalali_date()
    """

    SUPPORTED_LOCALES = frozenset({"fa_IR", "ar_SA", "en_US"})
    DEFAULT_LOCALE = "fa_IR"

    def __init__(
        self,
        locales: list[str] | str = "fa_IR",
        seed: int | None = None,
    ) -> None:
        if isinstance(locales, str):
            locales = [locales]
        self._locales = [loc for loc in locales if loc in self.SUPPORTED_LOCALES] or [self.DEFAULT_LOCALE]
        self._seed = seed

        import faker

        if len(self._locales) == 1:
            self._fake = faker.Faker(self._locales[0])
        else:
            self._fake = faker.Faker(self._locales)
        if seed is not None:
            self._fake.seed_instance(seed)

    def reseed(self, seed: int) -> None:
        self._fake.seed_instance(seed)

    def __getattr__(self, name: str) -> Any:
        return getattr(self._fake, name)

    def persian_name(self) -> str:
        """Generate a Persian full name."""
        return self._fake.name()

    def persian_first_name(self) -> str:
        return self._fake.first_name()

    def persian_last_name(self) -> str:
        return self._fake.last_name()

    def persian_company(self) -> str:
        return self._fake.company()

    def persian_phone(self) -> str:
        """Generate an Iranian mobile number (09xx xxx xxxx)."""
        prefixes = ["0912", "0913", "0914", "0915", "0916", "0917", "0918",
                    "0919", "0990", "0991", "0992", "0930", "0933", "0935",
                    "0936", "0937", "0938", "0939", "0901", "0902", "0903"]
        prefix = random.choice(prefixes)
        suffix = "".join(str(random.randint(0, 9)) for _ in range(7))
        return f"{prefix}{suffix}"

    def persian_national_id(self) -> str:
        """Generate a valid Iranian national ID (10 digits)."""
        digits = [random.randint(0, 9) for _ in range(9)]
        checksum = (
            sum(digits[8 - i] * (i + 2) for i in range(9)) % 11
        )
        control = 11 - checksum if checksum >= 2 else checksum
        digits.append(control if control < 10 else 0)
        return "".join(str(d) for d in digits)

    def persian_postal_code(self) -> str:
        return "".join(str(random.randint(0, 9)) for _ in range(10))

    def persian_city(self) -> str:
        cities = [
            "تهران", "مشهد", "اصفهان", "کرج", "شیراز", "تبریز", "قم",
            "اهواز", "کرمانشاه", "ارومیه", "رشت", "زاهدان", "همدان",
            "کرمان", "یزد", "اردبیل", "بندرعباس", "اراک", "اسلام‌شهر",
        ]
        return random.choice(cities)

    def persian_job_title(self) -> str:
        titles = [
            "مدیرعامل", "معاون", "مدیر", "سرپرست", "کارشناس ارشد",
            "کارشناس", "تحلیلگر", "برنامه‌نویس", "حسابدار", "مسئول دفتر",
        ]
        return random.choice(titles)

    def persian_address(self) -> str:
        return self._fake.address()

    def jalali_date(self, min_year: int = 1390, max_year: int = 1403) -> str:
        """Generate a random Jalali date string (YYYY/MM/DD)."""
        import jdatetime

        year = random.randint(min_year, max_year)
        month = random.randint(1, 12)
        day = random.randint(1, jdatetime.j_days_in_month[month - 1])
        if month == 12 and year % 4 != 3:
            day = min(day, 29)
        return f"{year:04d}/{month:02d}/{day:02d}"

    def jalali_date_obj(self, min_year: int = 1390, max_year: int = 1403) -> Any:
        """Generate a random Jalali date as jdatetime.date."""
        import jdatetime

        year = random.randint(min_year, max_year)
        month = random.randint(1, 12)
        day = random.randint(1, jdatetime.j_days_in_month[month - 1])
        if month == 12 and year % 4 != 3:
            day = min(day, 29)
        return jdatetime.date(year, month, day)

    def english_name(self) -> str:
        return self._fake.name()

    def english_company(self) -> str:
        return self._fake.company()

    def arabic_name(self) -> str:
        return self._fake.name()

    def random_element(self, *elements: Any) -> Any:
        return random.choice(elements)

    def random_int(self, min_val: int = 1, max_val: int = 100) -> int:
        return random.randint(min_val, max_val)

    def random_decimal(self, min_val: float = 0.0, max_val: float = 1000.0, precision: int = 2) -> float:
        return round(random.uniform(min_val, max_val), precision)

    def random_bool(self, true_prob: float = 0.5) -> bool:
        return random.random() < true_prob

    def unique_slug(self, prefix: str = "") -> str:
        import uuid
        base = prefix or "item"
        return f"{base}-{uuid.uuid4().hex[:8]}"

    def unique_email(self, domain: str = "example.com") -> str:
        import uuid
        return f"user-{uuid.uuid4().hex[:8]}@{domain}"

    def enum_value(self, choices: list[Any]) -> Any:
        return random.choice(choices)

    def pk_list(self, queryset, limit: int = 3) -> list[int]:
        """Pick random primary keys from a queryset."""
        ids = list(queryset.values_list("pk", flat=True))
        if not ids:
            return []
        k = min(limit, len(ids))
        return random.sample(ids, k=k)


def create_faker(
    locale: str = "fa_IR",
    seed: int | None = None,
) -> ScopedFaker:
    """Create a ScopedFaker instance for the given locale."""
    return ScopedFaker(locales=locale, seed=seed)


def iter_faker(
    locale: str = "fa_IR",
    seed: int | None = None,
    count: int = 10,
) -> Iterator[ScopedFaker]:
    """Yield count faker instances with incrementing seeds for reproducibility."""
    for i in range(count):
        yield ScopedFaker(locales=locale, seed=(seed or 0) + i)
