import json
from urllib.parse import parse_qs, urlencode, urlparse, urlunparse
from urllib.request import Request, urlopen


class XtreamError(Exception):
    pass


class XtreamCredentials:
    def __init__(self, base_url, username, password):
        self.base_url = base_url
        self.username = username
        self.password = password


def parse_xtream_url(value):
    parsed = urlparse(value.strip())
    query = parse_qs(parsed.query)
    username = query.get("username", [""])[0]
    password = query.get("password", [""])[0]
    if parsed.scheme not in {"http", "https"} or not parsed.netloc:
        raise XtreamError("La URL debe comenzar con http:// o https://")
    if not username or not password:
        raise XtreamError("La URL no contiene usuario y contraseña Xtream")
    base_url = urlunparse((parsed.scheme, parsed.netloc, "", "", "", "")).rstrip("/")
    return XtreamCredentials(base_url, username, password)


class XtreamClient:
    def __init__(self, credentials, timeout=15):
        self.credentials = credentials
        self.timeout = timeout

    def _request(self, action, extra_params=None):
        params = {
            "username": self.credentials.username,
            "password": self.credentials.password,
            "action": action,
        }
        if extra_params:
            params.update(extra_params)
        url = f"{self.credentials.base_url}/player_api.php?{urlencode(params)}"
        request = Request(url, headers={"User-Agent": "TuTV-Server/1.0"})
        try:
            with urlopen(request, timeout=self.timeout) as response:
                payload = json.loads(response.read().decode("utf-8", errors="replace"))
        except Exception as exc:
            raise XtreamError(f"No se pudo consultar el servidor Xtream: {exc}") from exc
        return payload

    def _get(self, action, extra_params=None):
        payload = self._request(action, extra_params)
        if not isinstance(payload, list):
            raise XtreamError("El servidor Xtream devolvió una respuesta inesperada")
        return payload

    def build_catalog(self):
        live_categories = self._get("get_live_categories")
        vod_categories = self._get("get_vod_categories")
        series_categories = self._get("get_series_categories")
        return {
            "live": {"categories": _categories(live_categories)},
            "movies": {"categories": _categories(vod_categories)},
            "series": {"categories": _categories(series_categories)},
            "generatedBy": "xtream",
        }

    def list_items(self, group, category_id, limit=250):
        actions = {
            "live": "get_live_streams",
            "movies": "get_vod_streams",
            "series": "get_series",
        }
        if group not in actions:
            raise XtreamError("Grupo inválido")
        payload = self._get(actions[group], {"category_id": category_id})
        return _items(group, payload[:limit], self.credentials)

    def series_info(self, series_id):
        payload = self._request("get_series_info", {"series_id": series_id})
        if not isinstance(payload, dict):
            raise XtreamError("El servidor Xtream devolvió una serie inesperada")
        return payload

    def list_series_seasons(self, series_id):
        payload = self.series_info(series_id)
        seasons = payload.get("seasons") or []
        if not isinstance(seasons, list):
            seasons = []
        result = []
        for index, season in enumerate(seasons, start=1):
            if not isinstance(season, dict):
                continue
            number = season.get("season_number") or season.get("number") or index
            result.append(
                {
                    "id": str(number),
                    "name": str(season.get("name") or "Temporada {}".format(number)),
                    "episodeCount": 0,
                }
            )
        episodes = payload.get("episodes") or {}
        if isinstance(episodes, dict):
            existing = {season["id"] for season in result}
            for number, items in episodes.items():
                if str(number) not in existing:
                    result.append(
                        {
                            "id": str(number),
                            "name": "Temporada {}".format(number),
                            "episodeCount": len(items) if isinstance(items, list) else 0,
                        }
                    )
        return result

    def list_series_episodes(self, series_id, season_id):
        payload = self.series_info(series_id)
        episodes = payload.get("episodes") or {}
        items = episodes.get(str(season_id), []) if isinstance(episodes, dict) else []
        if not isinstance(items, list):
            items = []
        result = []
        for item in items:
            if not isinstance(item, dict):
                continue
            episode_id = item.get("id")
            extension = str(item.get("container_extension") or "mp4").lstrip(".")
            stream_url = ""
            if episode_id:
                stream_url = "{}/series/{}/{}/{}.{}".format(
                    self.credentials.base_url,
                    self.credentials.username,
                    self.credentials.password,
                    episode_id,
                    extension,
                )
            result.append(
                {
                    "id": str(episode_id or ""),
                    "name": str(item.get("title") or item.get("name") or "Episodio"),
                    "image": str(item.get("info", {}).get("movie_image", "") if isinstance(item.get("info"), dict) else ""),
                    "streamUrl": stream_url,
                    "playable": bool(stream_url),
                }
            )
        return result


def _categories(items):
    return [
        {
            "id": str(item.get("category_id", "")),
            "name": str(item.get("category_name", "Sin categoría")),
        }
        for item in items
    ]


def _items(group, items, credentials):
    result = []
    for item in items:
        stream_url = ""
        if group == "series":
            item_id = item.get("series_id")
            image = item.get("cover", "")
        else:
            item_id = item.get("stream_id")
            image = item.get("stream_icon", "")
            if group == "live" and item_id:
                stream_url = "{}/live/{}/{}/{}.ts".format(
                    credentials.base_url,
                    credentials.username,
                    credentials.password,
                    item_id,
                )
            if group == "movies" and item_id:
                extension = str(item.get("container_extension") or "mp4").lstrip(".")
                stream_url = "{}/movie/{}/{}/{}.{}".format(
                    credentials.base_url,
                    credentials.username,
                    credentials.password,
                    item_id,
                    extension,
                )
        result.append(
            {
                "id": str(item_id or ""),
                "name": str(item.get("name", "Sin título")),
                "image": str(image or ""),
                "streamUrl": stream_url,
                "playable": bool(stream_url),
            }
        )
    return result


def pending_catalog():
    return {
        "live": {"categories": []},
        "movies": {"categories": []},
        "series": {"categories": []},
        "generatedBy": "pending-sync",
    }
