from __future__ import annotations

import logging
import re
from typing import Any

from .http import HttpClient
from .models import NormalizedMovie
from .utils import strip_season_from_title


class TmdbResolver:
    def __init__(self, config: dict[str, Any], http: HttpClient):
        self.config = config or {}
        self.http = http
        self.log = logging.getLogger(self.__class__.__name__)
        self.api_key = self.config.get("api_key") or ""
        self.read_access_token = self.config.get("read_access_token") or ""
        self.language = self.config.get("language", "vi-VN")
        self.image_base_url = self.config.get("image_base_url", "https://image.tmdb.org/t/p/original").rstrip("/")
        self.overrides = self.config.get("overrides", [])

    def apply(self, movie: NormalizedMovie) -> NormalizedMovie:
        override = self._find_override(movie)
        if override:
            return self.apply_mapping(movie, override)

        if movie.tmdb_id and self.config.get("enabled", True):
            self._apply_tmdb_metadata(movie)
        return movie

    def apply_mapping(self, movie: NormalizedMovie, mapping: dict[str, Any]) -> NormalizedMovie:
        movie.tmdb_id = str(mapping.get("tmdb_id") or movie.tmdb_id or "")
        movie.media_type = mapping.get("media_type") or movie.media_type
        season = mapping.get("season")
        if season is not None:
            self._set_episode_season(movie, int(season))
        if mapping.get("title"):
            movie.title = strip_season_from_title(str(mapping["title"]))
        if mapping.get("year"):
            movie.year = str(mapping["year"])
        self.log.info("Applied TMDB mapping source=%s slug=%s tmdb_id=%s", movie.source, movie.source_slug, movie.tmdb_id)
        if movie.tmdb_id and self.config.get("enabled", True):
            self._apply_tmdb_metadata(movie)
        return movie

    def _find_override(self, movie: NormalizedMovie) -> dict[str, Any] | None:
        source = movie.source.lower()
        slug = movie.source_slug.lower()
        for item in self.overrides:
            item_source = str(item.get("source") or "").lower()
            if item_source and item_source != source:
                continue
            item_slug = str(item.get("slug") or "").lower()
            if item_slug and item_slug == slug:
                return item
            pattern = item.get("slug_pattern")
            if pattern:
                match = re.match(str(pattern), slug, flags=re.IGNORECASE)
                if match:
                    resolved = dict(item)
                    season_group = item.get("season_group")
                    if season_group:
                        resolved["season"] = int(match.group(int(season_group)))
                    return resolved
        return None

    def _apply_tmdb_metadata(self, movie: NormalizedMovie) -> None:
        if not self.api_key and not self.read_access_token:
            return
        tmdb_type = "movie" if movie.media_type == "movie" else "tv"
        params, headers = self._auth()
        url = f"https://api.themoviedb.org/3/{tmdb_type}/{movie.tmdb_id}"
        try:
            data = self.http.get_json(url, params=params, headers=headers)
        except Exception as exc:
            self.log.warning("TMDB metadata lookup failed tmdb_id=%s type=%s error=%s", movie.tmdb_id, tmdb_type, exc)
            return

        if movie.media_type == "movie":
            title = data.get("title") or data.get("name")
            date = data.get("release_date") or ""
        else:
            title = data.get("name") or data.get("title")
            date = data.get("first_air_date") or ""
        if title:
            movie.title = strip_season_from_title(str(title))
        if date:
            movie.year = str(date).split("-", 1)[0]
        if data.get("original_title") or data.get("original_name"):
            movie.original_title = str(data.get("original_title") or data.get("original_name") or movie.original_title)
        if data.get("overview"):
            movie.content = str(data["overview"])
        if data.get("poster_path"):
            movie.poster_url = f"{self.image_base_url}{data['poster_path']}"
        if data.get("backdrop_path"):
            movie.backdrop_url = f"{self.image_base_url}{data['backdrop_path']}"
        if data.get("runtime") or data.get("episode_run_time"):
            runtime = data.get("runtime") or (data.get("episode_run_time") or [""])[0]
            movie.runtime = str(runtime)
        if data.get("genres"):
            movie.genres = [genre["name"] for genre in data["genres"] if genre.get("name")]
        external_ids = self.external_ids(movie.media_type, movie.tmdb_id)
        if external_ids.get("imdb_id"):
            movie.imdb_id = str(external_ids["imdb_id"])

    def external_ids(self, media_type: str, tmdb_id: str) -> dict[str, Any]:
        if not tmdb_id or (not self.api_key and not self.read_access_token):
            return {}
        tmdb_type = "movie" if media_type == "movie" else "tv"
        params, headers = self._auth(include_language=False)
        url = f"https://api.themoviedb.org/3/{tmdb_type}/{tmdb_id}/external_ids"
        try:
            return self.http.get_json(url, params=params, headers=headers)
        except Exception as exc:
            self.log.warning("TMDB external_ids lookup failed tmdb_id=%s type=%s error=%s", tmdb_id, tmdb_type, exc)
            return {}

    def _auth(self, include_language: bool = True) -> tuple[dict[str, str], dict[str, str]]:
        params = {"language": self.language} if include_language else {}
        headers = {}
        if self.read_access_token:
            headers["Authorization"] = f"Bearer {self.read_access_token}"
        else:
            params["api_key"] = self.api_key
        return params, headers

    def _set_episode_season(self, movie: NormalizedMovie, season: int) -> None:
        for server in movie.servers:
            for episode in server.episodes:
                episode.season = season
