from __future__ import annotations

import os
from dataclasses import dataclass, field
from pathlib import Path

from dotenv import load_dotenv


BACKEND_DIR = Path(__file__).resolve().parents[1]


def load_environment() -> None:
    load_dotenv(BACKEND_DIR / ".env", override=False)


@dataclass
class ProjectPaths:
    backend_dir: Path = BACKEND_DIR
    data_dir: Path = field(init=False)
    model_dir: Path = field(init=False)

    usernames: Path = field(init=False)
    usernames_cleaned: Path = field(init=False)
    anime_ids: Path = field(init=False)

    user_interactions: Path = field(init=False)
    legacy_user_genres: Path = field(init=False)
    user_genre_features: Path = field(init=False)

    anime_catalog: Path = field(init=False)
    legacy_anime_catalog: Path = field(init=False)
    legacy_anime_metadata: Path = field(init=False)
    legacy_anime_images: Path = field(init=False)

    anime_relationships: Path = field(init=False)
    legacy_anime_relationships: Path = field(init=False)

    atlas: Path = field(init=False)
    legacy_atlas: Path = field(init=False)

    model: Path = field(init=False)
    legacy_model: Path = field(init=False)
    dataset: Path = field(init=False)
    legacy_dataset: Path = field(init=False)
    training_report: Path = field(init=False)

    def __post_init__(self) -> None:
        self.data_dir = self.backend_dir / "data"
        self.model_dir = self.backend_dir / "model_files"

        self.usernames = self.data_dir / "usernames.txt"
        self.usernames_cleaned = self.data_dir / "usernames_cleaned.txt"
        self.anime_ids = self.data_dir / "anime_ids.txt"

        self.user_interactions = self.data_dir / "user_interactions.csv"
        self.legacy_user_genres = self.data_dir / "user_genre_data_nsfw.txt"
        self.user_genre_features = self.data_dir / "user_genre_features.csv"

        self.anime_catalog = self.data_dir / "anime_catalog.csv"
        self.legacy_anime_catalog = self.data_dir / "anime_data_master.csv"
        self.legacy_anime_metadata = self.data_dir / "anime_data.csv"
        self.legacy_anime_images = self.data_dir / "anime_img_data.csv"

        self.anime_relationships = self.data_dir / "anime_relationships.csv"
        self.legacy_anime_relationships = self.data_dir / "anime_relationship_data.csv"

        self.atlas = self.data_dir / "atlas.csv"
        self.legacy_atlas = self.data_dir / "atlas_data.csv"

        self.model = self.model_dir / "lightfm_model.pkl"
        self.legacy_model = self.model_dir / "lightfm_anime_model.pkl"
        self.dataset = self.model_dir / "lightfm_dataset.pkl"
        self.legacy_dataset = self.model_dir / "lightfm_anime_dataset.pkl"
        self.training_report = self.model_dir / "training_report.json"

    def ensure_directories(self) -> None:
        self.data_dir.mkdir(parents=True, exist_ok=True)
        self.model_dir.mkdir(parents=True, exist_ok=True)

    def _latest_matching(self, pattern: str) -> Path | None:
        matches = sorted(self.data_dir.glob(pattern))
        return matches[-1] if matches else None

    def resolve_user_interactions_input(self) -> Path:
        if self.user_interactions.exists():
            return self.user_interactions
        legacy = self._latest_matching("user_anime_data_v2_*.csv")
        return legacy if legacy is not None else self.user_interactions

    def resolve_anime_catalog_input(self) -> Path:
        if self.anime_catalog.exists():
            return self.anime_catalog
        return self.legacy_anime_catalog

    def resolve_anime_relationships_input(self) -> Path:
        if self.anime_relationships.exists():
            return self.anime_relationships
        return self.legacy_anime_relationships

    def resolve_atlas_input(self) -> Path:
        if self.atlas.exists():
            return self.atlas
        return self.legacy_atlas

    def resolve_model_input(self) -> Path:
        if self.model.exists():
            return self.model
        return self.legacy_model

    def resolve_dataset_input(self) -> Path:
        if self.dataset.exists():
            return self.dataset
        return self.legacy_dataset


@dataclass(frozen=True)
class RuntimeSettings:
    allowed_origins: tuple[str, ...] = (
        "http://localhost:3000",
        "http://127.0.0.1:3000",
        "http://3.138.171.23:3000",
        "https://anirec.ca",
        "https://www.anirec.ca",
        "http://anirec.ca",
        "http://www.anirec.ca",
    )
    bad_anime_ids: tuple[int, ...] = (51563, 52401, 52257, 51210, 5742, 50898)
    demographic_genres: tuple[str, ...] = (
        "Shounen",
        "Shoujo",
        "Seinen",
        "Josei",
        "Kodomo",
        "Award Winning",
        "Kids",
    )
    mal_env_var: str = "MAL_CLIENT_ID"


@dataclass(frozen=True)
class TrainingConfig:
    min_interactions_per_user: int = 5
    min_interactions_per_item: int = 3
    test_set_fraction: float = 0.1
    n_components: int = 30
    learning_rate: float = 0.05
    loss_function: str = "warp"
    epochs: int = 10
    threads: int = 1
    random_seed: int = 42


def get_mal_client_id() -> str:
    load_environment()
    client_id = os.getenv(RuntimeSettings().mal_env_var)
    if not client_id:
        raise RuntimeError(
            "Missing MAL client id. Set MAL_CLIENT_ID in backend/.env or the environment."
        )
    return client_id
