#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
resources/resolvers/ilcorsaronero.py
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Resolver per ilcorsaronero.link — lista torrent, ricerca, categorie,
estrazione magnet -> Elementum.

Espone le funzioni chiamate dal dispatcher:
    show_latest()          <- mode=icn_latest
    show_search()          <- mode=icn_search
    show_cats()            <- mode=icn_cats
    show_cat(url, title)   <- mode=icn_cat

PROBLEMA RISOLTO:
    requests.get() diretto viene bloccato da Cloudflare (challenge page).
    Il fetch ora usa 3 strategie in cascata:
      1. cloudscraper  (bypassa CF automaticamente)
      2. func.get() via TorManager (Tor bypassa CF da exit node diverso)
      3. requests + sessione con cookie CF (ultimo tentativo)
"""

import re as _re
import sys
import os

_ADDON_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
if _ADDON_ROOT not in sys.path:
    sys.path.insert(0, _ADDON_ROOT)

import variables as var
import functions as func
from models import Item

BASE_URL = 'https://ilcorsaronero.link'

# URL confermato funzionante (restituisce la tabella con i torrent)
_HOME_URL   = f'{BASE_URL}/?rc=explore&tp=1'
_LATEST_URL = f'{BASE_URL}/latest'

CATEGORIES = [
    ('[B][COLOR cyan]Film[/COLOR][/B]',          f'{BASE_URL}/cat/film'),
    ('[B][COLOR limegreen]Serie TV[/COLOR][/B]',  f'{BASE_URL}/cat/serie-tv'),
    ('[B][COLOR orange]Musica[/COLOR][/B]',        f'{BASE_URL}/cat/musica'),
    ('[B][COLOR yellow]Animazione[/COLOR][/B]',    f'{BASE_URL}/cat/animazione'),
    ('[B][COLOR gray]Altro[/COLOR][/B]',             f'{BASE_URL}/cat/altro'),
]

_UA = (
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
    'AppleWebKit/537.36 (KHTML, like Gecko) '
    'Chrome/124.0.0.0 Safari/537.36'
)
_HEADERS = {
    'User-Agent'     : _UA,
    'Accept'         : 'text/html,application/xhtml+xml;q=0.9,*/*;q=0.8',
    'Accept-Language': 'it-IT,it;q=0.9,en;q=0.8',
    'Referer'        : BASE_URL + '/',
}

# Tracker HTTP/HTTPS pubblici — stessa lista usata in thepiratebay.py.
# Elementum usa DHT (UDP) per risolvere i magnet, ma Tor non supporta UDP.
# Aggiungendo tracker HTTPS espliciti Elementum può contattarli via TCP
# anche attraverso Tor e ottenere i metadati entro il timeout di 60s.
from urllib.parse import quote as _q
_TRACKERS = [
    "udp://tracker.opentrackr.org:1337/announce",
    "udp://open.tracker.cl:1337/announce",
    "udp://tracker.openbittorrent.com:6969/announce",
    "https://tracker1.520.jp:443/announce",
    "https://opentracker.i2p.rocks:443/announce",
    "https://tracker.tamersunion.org:443/announce",
    "udp://open.demonii.com:1337/announce",
    "udp://tracker.torrent.eu.org:451/announce",
]
_TRACKER_PARAMS = "".join(f"&tr={_q(t)}" for t in _TRACKERS)


# Filtro video — estensioni e keyword che identificano contenuti NON video
_NON_VIDEO_EXT = (
    '.mp3', '.flac', '.ogg', '.aac', '.wav', '.opus', '.m4a', '.wma',
    '.epub', '.pdf', '.mobi', '.azw', '.djvu',
    '.zip', '.rar', '.7z', '.tar', '.gz', '.iso',
    '.exe', '.apk', '.dmg', '.deb', '.rpm',
    '.jpg', '.jpeg', '.png', '.gif', '.bmp',
    '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx',
)
_NON_VIDEO_KEYWORDS = (
    'audiobook', 'ebook', 'soundtrack', 'discography', 'album',
    'mp3', 'flac', 'epub', 'pdf', 'software', 'game', 'android',
)

def _is_video_title(title: str) -> bool:
    """True se il titolo sembra un contenuto video."""
    tl = title.lower()
    for ext in _NON_VIDEO_EXT:
        if tl.endswith(ext):
            return False
    for kw in _NON_VIDEO_KEYWORDS:
        if kw in tl:
            return False
    return True


# ─────────────────────────────────────────────────────────────────────
# FETCH  (3 strategie in cascata contro Cloudflare)
# ─────────────────────────────────────────────────────────────────────

def _fetch(url: str) -> str:
    """
    Scarica l'HTML con 3 strategie in cascata:
      1. cloudscraper  - bypassa Cloudflare JS challenge
      2. func.get()    - usa TorManager/bypass.py (Tor bypassa CF)
      3. requests      - diretto, ultimo tentativo
    """

    # ── Strategia 1: cloudscraper ─────────────────────────────────────
    try:
        import cloudscraper
        scraper = cloudscraper.create_scraper(
            browser={'browser': 'chrome', 'platform': 'windows', 'mobile': False}
        )
        r = scraper.get(url, headers=_HEADERS, timeout=20, allow_redirects=True)
        n = len(r.text)
        func.log(f'[icn] cloudscraper HTTP {r.status_code}, {n} byte: {url[:60]}')
        if r.status_code == 200 and n > 1000:
            if _has_torrent_links(r.text):
                return r.text
            func.log('[icn] cloudscraper: pagina senza link /torrent/ (CF challenge?)')
    except ImportError:
        func.log('[icn] cloudscraper non installato, provo TorManager')
    except Exception as e:
        func.log(f'[icn] cloudscraper errore: {e}')

    # ── Strategia 2: func.get() via TorManager/bypass ────────────────
    try:
        resp = func.get(url, headers=_HEADERS, timeout=20)
        n = len(resp.text)
        func.log(f'[icn] TorManager HTTP {resp.status_code}, {n} byte: {url[:60]}')
        if resp.status_code == 200 and n > 1000:
            if _has_torrent_links(resp.text):
                return resp.text
            func.log('[icn] TorManager: pagina senza link /torrent/')
    except Exception as e:
        func.log(f'[icn] TorManager errore: {e}')

    # ── Strategia 3: requests con sessione e cookie CF ────────────────
    try:
        import requests as _req
        sess = _req.Session()
        sess.get(BASE_URL, headers=_HEADERS, timeout=10, allow_redirects=True)
        r = sess.get(url, headers=_HEADERS, timeout=20, allow_redirects=True)
        n = len(r.text)
        func.log(f'[icn] requests+session HTTP {r.status_code}, {n} byte: {url[:60]}')
        if r.status_code == 200 and n > 1000:
            return r.text
    except Exception as e:
        func.log(f'[icn] requests+session errore: {e}')

    func.log(f'[icn] tutti i fetch falliti per: {url[:60]}')
    return ''


def _has_torrent_links(html: str) -> bool:
    """True se la pagina contiene almeno 1 link /torrent/."""
    return bool(_re.search(r'href="(/torrent/\d+/)', html))


# ─────────────────────────────────────────────────────────────────────
# PARSER  (BeautifulSoup + 2 fallback regex)
# ─────────────────────────────────────────────────────────────────────

def _parse_list(html: str) -> list:
    """
    Estrae torrent dalla tabella di ilcorsaronero.link.
    Tre strategie: BeautifulSoup, regex, fallback href puro.
    """
    if not html:
        return []

    results = []
    seen    = set()

    # ── Strategia 1: BeautifulSoup ────────────────────────────────────
    try:
        from bs4 import BeautifulSoup
        soup = BeautifulSoup(html, 'html.parser')
        pat  = _re.compile(r'^/torrent/\d+/', _re.I)

        for a in soup.find_all('a', href=pat):
            path = a.get('href', '').strip()
            if not path or path in seen:
                continue
            seen.add(path)

            # Filtra contenuti non-video
            raw_title = (a.get('title', '').strip()
                         or a.get_text(separator=' ', strip=True)
                         or path.rstrip('/').split('/')[-1].replace('-', ' '))
            if not _is_video_title(raw_title):
                continue

            title = (
                a.get('title', '').strip()
                or a.get_text(separator=' ', strip=True)
                or path.rstrip('/').split('/')[-1].replace('-', ' ')
            )[:80]
            if not title:
                continue

            seeds = 0
            size  = ''
            try:
                tr = a.find_parent('tr')
                if tr:
                    tds = tr.find_all('td')
                    for i, td in enumerate(tds):
                        if a in td.descendants:
                            for j, cand in enumerate(tds[i + 1:], i + 1):
                                txt = cand.get_text(strip=True)
                                if _re.match(r'^\d+$', txt):
                                    seeds = int(txt)
                                    for sz_td in tds[j + 1:]:
                                        sz = sz_td.get_text(strip=True)
                                        if sz and not _re.match(r'^\d+$', sz):
                                            size = sz
                                            break
                                    break
                            break
            except Exception:
                pass

            sc    = 'limegreen' if seeds > 5 else ('yellow' if seeds > 0 else 'red')
            label = (
                f'[B]{title}[/B]  [COLOR {sc}]S:{seeds}[/COLOR]'
                + (f'  [COLOR gray]{size}[/COLOR]' if size else '')
            )
            results.append({'title': label, 'url': BASE_URL + path,
                             'type': 'elementum', 'thumb': ''})

        func.log(f'[icn] BS4: {len(results)} trovati')

    except Exception as e:
        func.log(f'[icn] BS4 errore: {e}')

    # ── Strategia 2: regex ────────────────────────────────────────────
    if not results:
        func.log('[icn] fallback regex')
        PATTERN = (
            r'<a[^>]+href="(/torrent/\d+/[^"]+?)"'
            r'(?:[^>]*title="([^"]*)")?[^>]*>'
            r'.*?</a>'
            r'[^<]*</td>\s*'
            r'<td[^>]*>(\d+)</td>\s*'
            r'<td[^>]*>\d*</td>\s*'
            r'<td[^>]*>([^<]+)</td>'
        )
        for path, title_attr, seeds_s, size_s in _re.findall(PATTERN, html, _re.DOTALL):
            if path in seen:
                continue
            seen.add(path)
            raw_t = (title_attr.strip()
                     or path.rstrip('/').split('/')[-1].replace('-', ' '))
            if not _is_video_title(raw_t):
                continue
            title = raw_t[:80]
            try:
                s = int(seeds_s)
            except Exception:
                s = 0
            sc   = 'limegreen' if s > 5 else ('yellow' if s > 0 else 'red')
            size = size_s.strip()
            results.append({
                'title': f'[B]{title}[/B]  [COLOR {sc}]S:{s}[/COLOR]'
                         + (f'  [COLOR gray]{size}[/COLOR]' if size else ''),
                'url'  : BASE_URL + path,
                'type' : 'elementum', 'thumb': '',
            })
        func.log(f'[icn] regex: {len(results)} trovati')

    # ── Strategia 3: fallback href puro ──────────────────────────────
    if not results:
        func.log('[icn] fallback puro href')
        for path in _re.findall(r'href="(/torrent/\d+/[^"]+?)"', html):
            if path in seen:
                continue
            seen.add(path)
            slug = path.rstrip('/').split('/')[-1].replace('-', ' ')[:80]
            if not _is_video_title(slug):
                continue
            results.append({
                'title': f'[B]{slug}[/B]',
                'url'  : BASE_URL + path,
                'type' : 'elementum', 'thumb': '',
            })
        func.log(f'[icn] fallback puro: {len(results)} trovati')

    return results[:50]


# ─────────────────────────────────────────────────────────────────────
# ESTRATTORE MAGNET  (con tracker espliciti per aggirare UDP/Tor)
# ─────────────────────────────────────────────────────────────────────

def get_magnet(detail_url: str) -> str:
    """
    Fetcha la pagina dettaglio di un torrent ed estrae il magnet link.
    Aggiunge tracker HTTPS espliciti al magnet per permettere a Elementum
    di risolvere i metadati via TCP anche quando il traffico passa da Tor
    (Tor non supporta UDP, quindi DHT e tracker UDP non funzionano).
    """
    html = _fetch(detail_url)
    if not html:
        return ''

    # Magnet completo già nella pagina
    m = _re.search(r'(magnet:\?xt=urn:btih:[^\s"\'<&]+)', html, _re.I)
    if m:
        magnet = m.group(1).rstrip(')')
        # Aggiunge i tracker solo se non sono già presenti
        if '&tr=' not in magnet:
            magnet += _TRACKER_PARAMS
        func.log(f'[icn] magnet trovato: {magnet[:80]}')
        return magnet

    # Solo hash → ricostruisce magnet minimale con tracker
    h = _re.search(r'[Hh]ash[:\s`]*([0-9a-fA-F]{40})', html)
    if h:
        t   = _re.search(r'<h1[^>]*>\s*([^<]+)\s*</h1>', html)
        dn  = _q(t.group(1).strip()) if t else 'torrent'
        mag = f'magnet:?xt=urn:btih:{h.group(1)}&dn={dn}' + _TRACKER_PARAMS
        func.log(f'[icn] magnet da hash: {mag[:80]}')
        return mag

    func.log(f'[icn] magnet non trovato in: {detail_url[:60]}')
    return ''


# ─────────────────────────────────────────────────────────────────────
# PUBBLICO
# ─────────────────────────────────────────────────────────────────────

def show_list(url: str, title: str = '') -> None:
    """Fetcha una pagina lista e mostra i torrent con mode=onion_elementum."""
    from onion_xml_builder import build_listitem

    func.log(f'[icn] show_list: {url}')
    var.notify_dialog('IlCorsaroNero', 'Carico lista...', icon=var.addon_icon)

    html = _fetch(url)
    if not html:
        if url != _HOME_URL:
            func.log(f'[icn] riprovo con URL alternativo: {_HOME_URL}')
            html = _fetch(_HOME_URL)

    if not html:
        func.ok_dialog(
            f'Impossibile raggiungere ilcorsaronero.link\n'
            f'URL: {url}\n'
            'Controlla log Kodi per dettagli (filtra [icn]).'
        )
        return

    items = _parse_list(html)
    if not items:
        func.ok_dialog(
            'Nessun torrent trovato.\n'
            'La pagina potrebbe richiedere login o essere cambiata.'
        )
        return

    func.log(f'[icn] show_list OK: {len(items)} item')
    for entry in items:
        build_listitem(entry)


def show_latest() -> None:
    """Ultimi torrent. Prova /latest poi fallback su /?rc=explore&tp=1."""
    html = _fetch(_LATEST_URL)
    if not html or not _has_torrent_links(html):
        func.log('[icn] /latest vuoto, provo homepage /?rc=explore&tp=1')
        html = _fetch(_HOME_URL)

    if not html:
        func.ok_dialog('Impossibile raggiungere ilcorsaronero.link')
        return

    from onion_xml_builder import build_listitem
    var.notify_dialog('IlCorsaroNero', 'Carico ultimi...', icon=var.addon_icon)
    items = _parse_list(html)
    if not items:
        func.ok_dialog('Nessun torrent trovato. Controlla il log Kodi.')
        return
    for entry in items:
        build_listitem(entry)


def show_search() -> None:
    """Cerca su ilcorsaronero.link."""
    import xbmc as _xbmc
    from urllib.parse import quote_plus

    kb = _xbmc.Keyboard('', 'Cerca su IlCorsaroNero', False)
    kb.doModal()
    if not kb.isConfirmed():
        return
    query = kb.getText().strip()
    if not query:
        return

    var.notify_dialog('IlCorsaroNero', f'Cerca: {query}...', icon=var.addon_icon)
    q = quote_plus(query)

    candidates = [
        f'{BASE_URL}/search?q={q}',
        f'{BASE_URL}/search/{q}',
        f'{BASE_URL}/?q={q}',
        f'{BASE_URL}/search?query={q}',
        f'{BASE_URL}/?s={q}',
        f'{BASE_URL}/?rc=explore&tp=1',
    ]

    html  = ''
    used  = ''
    items = []

    for search_url in candidates:
        func.log(f'[icn] provo search: {search_url}')
        h = _fetch(search_url)
        if not h:
            continue
        n_links = len(_re.findall(r'href="(/torrent/\d+/[^"]+)"', h))
        func.log(f'[icn] {search_url}: {n_links} link torrent')
        if n_links < 1:
            continue

        if '?rc=explore' in search_url and query:
            partial = _parse_list(h)
            ql = query.lower().split()
            items = [
                it for it in partial
                if all(w in it['title'].lower() for w in ql)
            ]
            if items:
                used  = search_url + ' (filtrato per query)'
                html  = h
                break
            func.log('[icn] homepage filtrata: nessun match per la query')
            continue

        html = h
        used = search_url
        break

    if not html and not items:
        func.ok_dialog(
            f'Nessun risultato per: "{query}"\n'
            'Il sito potrebbe richiedere login per la ricerca.'
        )
        return

    if not items:
        items = _parse_list(html)

    if not items:
        func.ok_dialog(f'Nessun torrent per: "{query}"')
        return

    func.log(f'[icn] search OK da {used}: {len(items)} item')
    from onion_xml_builder import build_listitem
    for entry in items:
        build_listitem(entry)


def show_cats() -> None:
    """Categorie come cartelle navigabili."""
    for label, cat_url in CATEGORIES:
        func.create_listitem(Item(
            title=label,
            type='dir',
            mode='icn_cat',
            link=cat_url,
            thumbnail=var.addon_icon,
            is_media=False,
        ))


def show_cat(url: str, title: str = '') -> None:
    """Torrent di una categoria."""
    show_list(url, title)
