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

import html
import re
import time
from datetime import datetime
from urllib.parse import quote_plus

import requests
from bs4 import BeautifulSoup

from quasarr.constants import (
    CODEC_REGEX,
    RESOLUTION_REGEX,
    SEARCH_CAT_BOOKS,
    SEARCH_CAT_MOVIES,
    SEARCH_CAT_MUSIC,
    SEARCH_CAT_SHOWS,
    XXX_REGEX,
)
from quasarr.providers.hostname_issues import clear_hostname_issue, mark_hostname_issue
from quasarr.providers.imdb_metadata import get_localized_title, get_year
from quasarr.providers.log import debug, error, info, warn
from quasarr.providers.utils import (
    generate_download_link,
    get_base_search_category_id,
    is_imdb_id,
    is_valid_release,
    normalize_magazine_title,
)

hostname = "by"


def convert_to_rss_date(date_str):
    """
    BY date format: 'dd.mm.yy HH:MM', e.g. '20.07.25 17:48'
    """
    try:
        dt_obj = datetime.strptime(date_str, "%d.%m.%y %H:%M")
        return dt_obj.strftime("%a, %d %b %Y %H:%M:%S +0000")
    except:
        return ""


def extract_size(text):
    m = re.match(r"(\d+(?:[.,]\d+)?)\s*([A-Za-z]+)", text)
    if not m:
        raise ValueError(f"Invalid size format: {text!r}")
    size_str = m.group(1).replace(",", ".")
    sizeunit = m.group(2)
    size_float = float(size_str)  # convert to float here
    return {"size": size_float, "sizeunit": sizeunit}


def _parse_posts(
    soup,
    shared_state,
    base_url,
    password,
    is_search=False,
    search_category=None,
    search_string=None,
    season=None,
    episode=None,
):
    releases = []

    base_category = get_base_search_category_id(search_category)

    if not is_search:
        feed_container = soup.find(
            "table", class_="AUDIO_ITEMLIST"
        )  # it is actually called this way
        candidates = []
        if feed_container:
            for tbl in feed_container.find_all("table"):
                if tbl.find(string=re.compile(r"Erstellt am:")):
                    candidates.append(tbl)

        # --- FALLBACK LOGIC ---
        # If the standard audio list logic found nothing, check the default list layout
        if not candidates:
            feed_container = soup.find("table", class_="DEFAULT_ITEMLIST")
            if feed_container:
                # This layout uses TRs for items instead of nested Tables
                # We filter by looking for the "TITLE" paragraph class unique to content rows
                for tr in feed_container.find_all("tr"):
                    if tr.find("p", class_="TITLE"):
                        candidates.append(tr)

        items = candidates
    else:
        search_table = soup.find("table", class_="SEARCH_ITEMLIST")
        items = []
        if search_table:
            items = [
                tr
                for tr in search_table.find_all("tr")
                if tr.find("p", class_="TITLE")
                and tr.find("p", class_="TITLE").find("a", href=True)
            ]

    for entry in items:
        if entry.find("table"):
            continue  # Skip header rows
        try:
            if not is_search:
                table = entry
                # title & source
                try:
                    link_tag = table.find("th").find("a")
                except AttributeError:
                    link_tag = table.find("a")
                title = link_tag.get_text(strip=True)
                if not title:
                    try:
                        title = link_tag.get("title", "")
                    except:
                        pass
                if not title:
                    continue
                if base_category == SEARCH_CAT_BOOKS:
                    # lazylibrarian can only detect specific date formats / issue numbering for magazines
                    title = normalize_magazine_title(title)
                else:
                    title = title.replace(" ", ".")

                source = base_url + link_tag["href"]
                # extract date and size
                date_str = size_str = None
                for row in table.find_all("tr", height=True):
                    cols = row.find_all("td")
                    if len(cols) == 2:
                        label = cols[0].get_text(strip=True)
                        val = cols[1].get_text(strip=True)
                        if label.startswith("Erstellt am"):
                            date_str = val
                        elif label.startswith("Größe"):
                            size_str = val

                if not date_str:
                    cols = entry.find_all("td")
                    if len(cols) >= 2:
                        val = cols[1].get_text(strip=True)
                        if re.match(r"\d{2}\.\d{2}\.\d{2} \d{2}:\d{2}", val):
                            date_str = val

                if not size_str:
                    cols = entry.find_all("td")
                    for col in cols:
                        val = col.get_text(strip=True)
                        if re.match(r"\d+(?:[.,]\d+)?\s*[A-Za-z]+", val):
                            size_str = val
                            break

                published = convert_to_rss_date(date_str) if date_str else ""
                if not published:
                    debug("fuck")
                size_info = (
                    extract_size(size_str)
                    if size_str
                    else {"size": "0", "sizeunit": "MB"}
                )
                mb = float(size_info["size"])
                size_bytes = int(mb * 1024 * 1024)
                imdb_id = None
            else:
                row = entry
                title_tag = row.find("p", class_="TITLE").find("a")
                title = title_tag.get_text(strip=True)
                if base_category == SEARCH_CAT_BOOKS:
                    # lazylibrarian can only detect specific date formats / issue numbering for magazines
                    title = normalize_magazine_title(title)
                else:
                    title = title.replace(" ", ".")
                    if not (
                        RESOLUTION_REGEX.search(title) or CODEC_REGEX.search(title)
                    ):
                        continue

                if not is_valid_release(
                    title, base_category, search_string, season, episode
                ):
                    continue
                if XXX_REGEX.search(title) and "xxx" not in search_string.lower():
                    continue

                source = base_url + title_tag["href"]
                date_cell = row.find_all("td")[2]
                date_str = date_cell.get_text(strip=True)
                if not re.match(r"\d{2}\.\d{2}\.\d{2} \d{2}:\d{2}", date_str):
                    cols = row.find_all("td")
                    if len(cols) >= 2:
                        val = cols[1].get_text(strip=True)
                        if re.match(r"\d{2}\.\d{2}\.\d{2} \d{2}:\d{2}", val):
                            date_str = val
                published = convert_to_rss_date(date_str)
                size_bytes = 0
                mb = 0
                imdb_id = None

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

            releases.append(
                {
                    "details": {
                        "title": title,
                        "hostname": hostname,
                        "imdb_id": imdb_id,
                        "link": link,
                        "size": size_bytes,
                        "date": published,
                        "source": source,
                    },
                    "type": "protected",
                }
            )
        except Exception as e:
            debug(f"Error parsing: {e}")
            continue

    return releases


def by_feed(shared_state, start_time, search_category):
    by = shared_state.values["config"]("Hostnames").get(hostname)
    password = by

    base_category = get_base_search_category_id(search_category)

    if base_category == SEARCH_CAT_BOOKS:
        feed_type = "?cat=71"
    elif base_category == SEARCH_CAT_MOVIES:
        feed_type = "?cat=1"
    elif base_category == SEARCH_CAT_SHOWS:
        feed_type = "?cat=2"
    elif base_category == SEARCH_CAT_MUSIC:
        feed_type = "?cat=35"
    else:
        warn(f"Invalid search category: {search_category}")
        return []

    base_url = f"https://{by}"
    url = f"{base_url}/{feed_type}"
    headers = {"User-Agent": shared_state.values["user_agent"]}
    try:
        r = requests.get(url, headers=headers, timeout=30)
        r.raise_for_status()
        soup = BeautifulSoup(r.content, "html.parser")
        releases = _parse_posts(
            soup,
            shared_state,
            base_url,
            password,
            search_category=search_category,
        )
    except Exception as e:
        error(f"Error loading feed: {e}")
        mark_hostname_issue(
            hostname, "feed", str(e) if "e" in dir() else "Error occurred"
        )
        releases = []
    debug(f"Time taken: {time.time() - start_time:.2f}s")

    if releases:
        clear_hostname_issue(hostname)
    return releases


def by_search(
    shared_state,
    start_time,
    search_category,
    search_string,
    season=None,
    episode=None,
):
    by = shared_state.values["config"]("Hostnames").get(hostname)
    password = by

    imdb_id = is_imdb_id(search_string)
    if imdb_id:
        title = get_localized_title(shared_state, imdb_id, "de")
        if not title:
            info(f"Could not extract title from IMDb-ID {imdb_id}")
            return []
        search_string = html.unescape(title)
        if not season:
            if year := get_year(imdb_id):
                search_string += f" {year}"

    base_url = f"https://{by}"
    q = quote_plus(search_string)
    url = f"{base_url}/?q={q}"
    headers = {"User-Agent": shared_state.values["user_agent"]}
    try:
        r = requests.get(url, headers=headers, timeout=10)
        r.raise_for_status()
        soup = BeautifulSoup(r.content, "html.parser")
        releases = _parse_posts(
            soup,
            shared_state,
            base_url,
            password,
            is_search=True,
            search_category=search_category,
            search_string=search_string,
            season=season,
            episode=episode,
        )
    except Exception as e:
        error(f"Error loading search: {e}")
        mark_hostname_issue(
            hostname, "search", str(e) if "e" in dir() else "Error occurred"
        )
        releases = []
    debug(f"Time taken: {time.time() - start_time:.2f}s")

    if releases:
        clear_hostname_issue(hostname)
    return releases
