from __future__ import annotations

from typing import Optional, Union

import requests

from envied.core.config import config
from envied.core.providers._base import ExternalIds, MetadataProvider, MetadataResult, fuzzy_match


class SimklProvider(MetadataProvider):
    """SIMKL metadata provider (filename-based search)."""

    NAME = "simkl"
    REQUIRES_KEY = True
    BASE_URL = "https://api.simkl.com"

    def is_available(self) -> bool:
        return bool(config.simkl_client_id)

    def search(self, title: str, year: Optional[int], kind: str) -> Optional[MetadataResult]:
        self.log.debug("Searching Simkl for %r (%s, %s)", title, kind, year)

        # Construct appropriate filename based on type
        filename = f"{title}"
        if year:
            filename = f"{title} {year}"
        if kind == "tv":
            filename += " S01E01.mkv"
        else:
            filename += " 2160p.mkv"

        try:
            headers = {"simkl-api-key": config.simkl_client_id}
            resp = self.session.post(
                f"{self.BASE_URL}/search/file", json={"file": filename}, headers=headers, timeout=30
            )
            resp.raise_for_status()
            data = resp.json()
            self.log.debug("Simkl API response received")
        except (requests.RequestException, ValueError) as exc:
            self.log.debug("Simkl search failed: %s", exc)
            return None

        # Handle case where SIMKL returns empty list (no results)
        if isinstance(data, list):
            self.log.debug("Simkl returned list (no matches) for %r", filename)
            return None

        return self._parse_response(data, title, year, kind)

    def get_by_id(self, provider_id: Union[int, str], kind: str) -> Optional[MetadataResult]:
        return None  # SIMKL has no direct ID lookup used here

    def get_external_ids(self, provider_id: Union[int, str], kind: str) -> ExternalIds:
        return ExternalIds()  # IDs come from search() response

    def find_by_imdb_id(self, imdb_id: str, kind: str) -> Optional[ExternalIds]:
        """Look up TMDB/TVDB IDs from an IMDB ID using SIMKL's /search/id and detail endpoints."""
        self.log.debug("Looking up IMDB ID %s on SIMKL", imdb_id)
        headers = {"simkl-api-key": config.simkl_client_id}

        try:
            r = self.session.get(f"{self.BASE_URL}/search/id", params={"imdb": imdb_id}, headers=headers, timeout=30)
            r.raise_for_status()
            data = r.json()
        except (requests.RequestException, ValueError) as exc:
            self.log.debug("SIMKL search/id failed: %s", exc)
            return None

        if not isinstance(data, list) or not data:
            self.log.debug("No SIMKL results for IMDB ID %s", imdb_id)
            return None

        entry = data[0]
        simkl_id = entry.get("ids", {}).get("simkl")
        if not simkl_id:
            return None

        # Map SIMKL type to endpoint
        simkl_type = entry.get("type", "")
        endpoint = "tv" if simkl_type in ("tv", "anime") else "movies"

        # Fetch full details to get cross-referenced IDs
        try:
            r2 = self.session.get(
                f"{self.BASE_URL}/{endpoint}/{simkl_id}",
                params={"extended": "full"},
                headers=headers,
                timeout=30,
            )
            r2.raise_for_status()
            detail = r2.json()
        except (requests.RequestException, ValueError) as exc:
            self.log.debug("SIMKL detail fetch failed: %s", exc)
            return None

        ids = detail.get("ids", {})
        tmdb_id: Optional[int] = None
        raw_tmdb = ids.get("tmdb")
        if raw_tmdb:
            tmdb_id = int(raw_tmdb)

        tvdb_id: Optional[int] = None
        raw_tvdb = ids.get("tvdb")
        if raw_tvdb:
            tvdb_id = int(raw_tvdb)

        self.log.debug("SIMKL find -> TMDB %s, TVDB %s for IMDB %s", tmdb_id, tvdb_id, imdb_id)

        return ExternalIds(
            imdb_id=imdb_id,
            tmdb_id=tmdb_id,
            tmdb_kind=kind,
            tvdb_id=tvdb_id,
        )

    def _parse_response(
        self, data: dict, search_title: str, search_year: Optional[int], kind: str
    ) -> Optional[MetadataResult]:
        """Parse a SIMKL response into a MetadataResult."""
        if data.get("type") == "episode" and "show" in data:
            info = data["show"]
            content_type = "tv"
        elif data.get("type") == "movie" and "movie" in data:
            info = data["movie"]
            content_type = "movie"
        else:
            return None

        result_title = info.get("title")
        result_year = info.get("year")

        # Verify title matches
        if not result_title or not fuzzy_match(result_title, search_title):
            self.log.debug("Simkl title mismatch: searched %r, got %r", search_title, result_title)
            return None

        # Verify year if provided (allow 1 year difference)
        if search_year and result_year and abs(search_year - result_year) > 1:
            self.log.debug("Simkl year mismatch: searched %d, got %d", search_year, result_year)
            return None

        ids = info.get("ids", {})
        tmdb_id: Optional[int] = None
        if content_type == "tv":
            raw_tmdb = ids.get("tmdbtv")
        else:
            raw_tmdb = ids.get("tmdb") or ids.get("moviedb")
        if raw_tmdb:
            tmdb_id = int(raw_tmdb)

        tvdb_id: Optional[int] = None
        raw_tvdb = ids.get("tvdb")
        if raw_tvdb:
            tvdb_id = int(raw_tvdb)

        self.log.debug("Simkl -> %s (TMDB ID %s)", result_title, tmdb_id)

        return MetadataResult(
            title=result_title,
            year=result_year,
            kind=kind,
            external_ids=ExternalIds(
                imdb_id=ids.get("imdb"),
                tmdb_id=tmdb_id,
                tmdb_kind=kind,
                tvdb_id=tvdb_id,
            ),
            source="simkl",
            raw=data,
        )
