# -*- coding: utf-8 -*-
# Quasarr
# Project by https://github.com/rix1337

import datetime
import html
import re
import time
import xml.etree.ElementTree as ET
from concurrent.futures import ThreadPoolExecutor, as_completed
from urllib.parse import quote_plus

import requests
from bs4 import BeautifulSoup

from quasarr.constants import (
    SEARCH_CAT_BOOKS,
    SEARCH_CAT_MOVIES,
    SEARCH_CAT_MUSIC,
    SEARCH_CAT_SHOWS,
)
from quasarr.providers.cloudflare import ensure_session_cf_bypassed
from quasarr.providers.hostname_issues import clear_hostname_issue, mark_hostname_issue
from quasarr.providers.imdb_metadata import get_localized_title
from quasarr.providers.log import debug, info, warn
from quasarr.providers.utils import (
    convert_to_mb,
    generate_download_link,
    get_base_search_category_id,
    is_imdb_id,
    is_valid_release,
    normalize_magazine_title,
)

hostname = "sl"


def extract_size(text):
    match = re.match(r"([\d\.]+)\s*([KMGT]B)", text, re.IGNORECASE)
    if match:
        size = match.group(1)
        unit = match.group(2).upper()
        return {"size": size, "sizeunit": unit}
    else:
        raise ValueError(f"Invalid size format: {text}")


def parse_pubdate_to_iso(pubdate_str):
    """
    Parse an RFC-822 pubDate from RSS into an ISO8601 string with timezone.
    """
    dt = datetime.datetime.strptime(pubdate_str, "%a, %d %b %Y %H:%M:%S %z")
    return dt.isoformat()


def sl_feed(shared_state, start_time, search_category):
    releases = []

    sl = shared_state.values["config"]("Hostnames").get(hostname.lower())
    password = sl

    base_category = get_base_search_category_id(search_category)

    if base_category == SEARCH_CAT_BOOKS:
        feed_type = "ebooks"
    elif base_category == SEARCH_CAT_MOVIES:
        feed_type = "movies"
    elif base_category == SEARCH_CAT_SHOWS:
        feed_type = "tv-shows"
    elif base_category == SEARCH_CAT_MUSIC:
        feed_type = "music"
    else:
        warn(f"Unknown search category: {search_category}")
        return releases

    url = f"https://{sl}/{feed_type}/feed/"
    headers = {"User-Agent": shared_state.values["user_agent"]}

    try:
        session = requests.Session()
        session, headers, r = ensure_session_cf_bypassed(
            info, shared_state, session, url, headers
        )
        if not r:
            raise requests.RequestException("Cloudflare bypass failed")

        r.raise_for_status()
        root = ET.fromstring(r.text)

        for item in root.find("channel").findall("item"):
            try:
                title = item.findtext("title").strip()
                if base_category == SEARCH_CAT_BOOKS:
                    # lazylibrarian can only detect specific date formats / issue numbering for magazines
                    title = normalize_magazine_title(title)

                source = item.findtext("link").strip()

                desc = item.findtext("description") or ""

                size_match = re.search(
                    r"Size:\s*([\d\.]+\s*(?:GB|MB|KB|TB))", desc, re.IGNORECASE
                )
                if not size_match:
                    debug(f"Size not found in RSS item: {title}")
                    continue
                size_info = size_match.group(1).strip()
                size_item = extract_size(size_info)
                mb = convert_to_mb(size_item)
                size = mb * 1024 * 1024

                pubdate = item.findtext("pubDate").strip()
                published = parse_pubdate_to_iso(pubdate)

                m = re.search(r"https?://www\.imdb\.com/title/(tt\d+)", desc)
                imdb_id = m.group(1) if m else None

                link = generate_download_link(
                    shared_state,
                    title,
                    source,
                    mb,
                    password,
                    imdb_id,
                    hostname,
                )

                releases.append(
                    {
                        "details": {
                            "title": title,
                            "hostname": hostname.lower(),
                            "imdb_id": imdb_id,
                            "link": link,
                            "size": size,
                            "date": published,
                            "source": source,
                        },
                        "type": "protected",
                    }
                )

            except Exception as e:
                warn(f"Error parsing {hostname.upper()} feed item: {e}")
                mark_hostname_issue(
                    hostname, "feed", str(e) if "e" in dir() else "Error occurred"
                )
                continue

    except Exception as e:
        warn(f"Error loading {hostname.upper()} feed: {e}")
        mark_hostname_issue(
            hostname, "feed", str(e) if "e" in dir() else "Error occurred"
        )

    elapsed = time.time() - start_time
    debug(f"Time taken: {elapsed:.2f}s")

    if releases:
        clear_hostname_issue(hostname)
    return releases


def sl_search(
    shared_state,
    start_time,
    search_category,
    search_string,
    season=None,
    episode=None,
):
    releases = []
    sl = shared_state.values["config"]("Hostnames").get(hostname.lower())
    password = sl

    base_category = get_base_search_category_id(search_category)

    if base_category == SEARCH_CAT_BOOKS:
        feed_type = "ebooks"
    elif base_category == SEARCH_CAT_MOVIES:
        feed_type = "movies"
    elif base_category == SEARCH_CAT_SHOWS:
        feed_type = "tv-shows"
    elif base_category == SEARCH_CAT_MUSIC:
        feed_type = "music"
    else:
        warn(f"Unknown search category: {search_category}")
        return releases

    try:
        imdb_id = is_imdb_id(search_string)
        if imdb_id:
            search_string = get_localized_title(shared_state, imdb_id, "en") or ""
            search_string = html.unescape(search_string)
            if not search_string:
                info(f"Could not extract title from IMDb-ID {imdb_id}")
                return releases

        # Build the list of URLs to search. For tv-shows also search the "foreign" section.
        q = quote_plus(search_string)
        urls = [f"https://{sl}/{feed_type}/?s={q}"]
        if feed_type == "tv-shows":
            urls.append(f"https://{sl}/foreign/?s={q}")

        headers = {"User-Agent": shared_state.values["user_agent"]}

        # Fetch pages in parallel (so we don't double the slow site latency)
        def fetch(url):
            try:
                debug(f"Fetching {url}")
                session = requests.Session()
                session, _, r = ensure_session_cf_bypassed(
                    info, shared_state, session, url, headers
                )
                if not r:
                    raise requests.RequestException("Cloudflare bypass failed")
                r.raise_for_status()
                return r.text
            except Exception as e:
                info(f"Error fetching url {url}: {e}")
                mark_hostname_issue(
                    hostname, "search", str(e) if "e" in dir() else "Error occurred"
                )
                return ""

        html_texts = []
        with ThreadPoolExecutor(max_workers=len(urls)) as tpe:
            futures = {tpe.submit(fetch, u): u for u in urls}
            for future in as_completed(futures):
                try:
                    html_texts.append(future.result())
                except Exception as e:
                    warn(f"Error fetching search page: {e}")
                    mark_hostname_issue(
                        hostname, "search", str(e) if "e" in dir() else "Error occurred"
                    )

        # Parse each result and collect unique releases (dedupe by source link)
        seen_sources = set()
        for html_text in html_texts:
            if not html_text:
                continue
            try:
                soup = BeautifulSoup(html_text, "html.parser")
                posts = soup.find_all(
                    "div", class_=lambda c: c and c.startswith("post-")
                )

                for post in posts:
                    try:
                        a = post.find("h1").find("a")
                        title = a.get_text(strip=True)

                        if not is_valid_release(
                            title, base_category, search_string, season, episode
                        ):
                            continue

                        if base_category == SEARCH_CAT_BOOKS:
                            title = normalize_magazine_title(title)
                            imdb_id = None

                        source = a["href"]
                        # dedupe
                        if source in seen_sources:
                            continue
                        seen_sources.add(source)

                        # Published date
                        time_tag = post.find("span", {"class": "localtime"})
                        published = None
                        if time_tag and time_tag.has_attr("data-lttime"):
                            published = time_tag["data-lttime"]
                        published = (
                            published
                            or datetime.datetime.utcnow().isoformat() + "+00:00"
                        )

                        size = 0

                        link = generate_download_link(
                            shared_state,
                            title,
                            source,
                            0,
                            password,
                            imdb_id,
                            hostname,
                        )

                        releases.append(
                            {
                                "details": {
                                    "title": title,
                                    "hostname": hostname.lower(),
                                    "imdb_id": imdb_id,
                                    "link": link,
                                    "size": size,
                                    "date": published,
                                    "source": source,
                                },
                                "type": "protected",
                            }
                        )
                    except Exception as e:
                        warn(f"Error parsing {hostname.upper()} search item: {e}")
                        mark_hostname_issue(
                            hostname,
                            "search",
                            str(e) if "e" in dir() else "Error occurred",
                        )
                        continue
            except Exception as e:
                warn(f"Error parsing {hostname.upper()} search HTML: {e}")
                mark_hostname_issue(
                    hostname, "search", str(e) if "e" in dir() else "Error occurred"
                )
                continue

    except Exception as e:
        warn(f"Error loading {hostname.upper()} search page: {e}")
        mark_hostname_issue(
            hostname, "search", str(e) if "e" in dir() else "Error occurred"
        )

    elapsed = time.time() - start_time
    debug(f"Search time: {elapsed:.2f}s")

    if releases:
        clear_hostname_issue(hostname)
    return releases
