import concurrent.futures
from typing import Any, Callable, Dict, List, Optional

from lib.clients.base import BaseClient, TorrentStream
from lib.jacktook.utils import kodilog
from lib.utils.kodi.logging import summarize_locator_for_log
from lib.utils.kodi.settings import get_prowlarr_timeout
from lib.utils.kodi.utils import get_setting, notification, translation


class Prowlarr(BaseClient):
    def __init__(self, host: str, apikey: str, port: str, notification: Callable) -> None:
        super().__init__(host, notification)
        self.base_url = f"{self.host}:{port}/api/v1/search"
        self.apikey = apikey

    def search(
        self,
        query: str,
        mode: str,
        season: Optional[int] = None,
        episode: Optional[int] = None,
        indexers: Optional[str] = None,
        variant=None,
        year: Optional[int] = None,
    ) -> Optional[List[TorrentStream]]:
        headers = {
            "Accept": "application/json",
            "Content-Type": "application/json",
            "X-Api-Key": self.apikey,
        }
        try:
            season = self._coerce_int(season)
            episode = self._coerce_int(episode)
            results = []
            futures = []

            if mode == "movies" and year:
                query_with_year = f"{query} {year}"
            else:
                query_with_year = query

            with concurrent.futures.ThreadPoolExecutor() as executor:
                if mode == "tv" and season:
                    if episode:
                        params_ep: Dict[str, Any] = {
                            "query": f"{query} S{season:02d}E{episode:02d}",
                            "type": "search",
                            "categories": [5000, 8000],
                        }
                        if indexers:
                            indexer_ids = [
                                i.strip() for i in indexers.replace(",", " ").split() if i.strip()
                            ]
                            if indexer_ids:
                                params_ep["indexerIds"] = indexer_ids
                        futures.append(
                            executor.submit(
                                self.session.get,
                                self.base_url,
                                params=params_ep,
                                timeout=get_prowlarr_timeout(),
                                headers=headers,
                            )
                        )
                    # Season pack query
                    if get_setting("include_season_packs"):
                        params_season: Dict[str, Any] = {
                            "query": f"{query} S{season:02d}",
                            "type": "search",
                            "categories": [5000, 8000],
                        }
                        if indexers:
                            indexer_ids = [
                                i.strip() for i in indexers.replace(",", " ").split() if i.strip()
                            ]
                            if indexer_ids:
                                params_season["indexerIds"] = indexer_ids
                        futures.append(
                            executor.submit(
                                self.session.get,
                                self.base_url,
                                params=params_season,
                                timeout=get_prowlarr_timeout(),
                                headers=headers,
                            )
                        )
                else:
                    params: Dict[str, Any] = {
                        "query": query_with_year,
                        "type": "search",
                    }
                    if mode == "movies":
                        params["categories"] = [2000, 8000]
                    elif mode == "anime":
                        params["categories"] = [2000, 5070, 5000, 127720, 140679]
                    if indexers:
                        indexer_ids = [
                            i.strip() for i in indexers.replace(",", " ").split() if i.strip()
                        ]
                        if indexer_ids:
                            params["indexerIds"] = indexer_ids
                    futures.append(
                        executor.submit(
                            self.session.get,
                            self.base_url,
                            params=params,
                            timeout=get_prowlarr_timeout(),
                            headers=headers,
                        )
                    )

                for future in concurrent.futures.as_completed(futures):
                    response = future.result()
                    if response.status_code != 200:
                        notification(f"{translation(30230)} {response.status_code}")
                    else:
                        res = self.parse_response(response)
                        if res:
                            results.extend(res)
            return results if results else None
        except Exception as e:
            self.handle_exception(f"{translation(30230)}: {e!s}")
            return None

    @staticmethod
    def _coerce_int(value: Any) -> Optional[int]:
        try:
            return int(value)
        except (TypeError, ValueError):
            return None

    def parse_response(self, res: Any) -> List[TorrentStream]:
        response = res.json()
        results = []
        for res in response:
            title = res.get("title", "")
            provider = res.get("indexer")
            guid = res.get("guid", "")
            download_url = res.get("downloadUrl", "")
            magnet_url = res.get("magnetUrl", "")
            info_url = res.get("infoUrl", "")
            info_hash = res.get("infoHash", "")

            kodilog(
                "Prowlarr parsed result: title={!r}, provider={!r}, guid={!r}, download={!r}, magnet={!r}, info={!r}, infohash={!r}, has_magnet={}, has_http_url={}, has_infohash={}".format(
                    title,
                    provider,
                    summarize_locator_for_log(guid),
                    summarize_locator_for_log(download_url),
                    summarize_locator_for_log(magnet_url),
                    summarize_locator_for_log(info_url),
                    str(info_hash).lower()[:12],
                    bool(
                        str(magnet_url).startswith("magnet:?")
                        or str(download_url).startswith("magnet:?")
                        or str(guid).startswith("magnet:?")
                    ),
                    bool(
                        str(download_url).startswith(("http://", "https://"))
                        or str(info_url).startswith(("http://", "https://"))
                        or str(guid).startswith(("http://", "https://"))
                    ),
                    bool(info_hash),
                )
            )

            results.append(
                TorrentStream(
                    title=title,
                    type="Torrent",
                    indexer="Prowlarr",
                    provider=provider,
                    peers=int(res.get("peers", 0)),
                    seeders=int(res.get("seeders", 0)),
                    guid=guid,
                    infoHash=info_hash,
                    size=int(res.get("size", 0)),
                    languages=res.get("languages", []),
                    fullLanguages=res.get("fullLanguages", ""),
                    publishDate=res.get("publishDate", ""),
                    url=download_url,
                )
            )
        return results
