# -*- coding: utf-8 -*-

WEBSITE = 'HINATASOUL'

import re
import difflib
import unicodedata
from urllib.parse import quote_plus, urljoin, urlparse, parse_qs, unquote
from bs4 import BeautifulSoup
import requests

from resources.lib.resolver import Resolver

try:
    import xbmcaddon
    addon = xbmcaddon.Addon()
    DUBBED = addon.getLocalizedString(30200)
    SUBTITLED = addon.getLocalizedString(30202)
except:
    DUBBED = 'DUBLADO'
    SUBTITLED = 'LEGENDADO'

session = requests.Session()
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36'
session.headers.update({
    'User-Agent': USER_AGENT,
    'Accept-Language': 'en-US,en;q=0.9,pt-BR;q=0.8,pt;q=0.7',
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Referer': 'https://www.hinatasoulbr.vip/',
})


class source:

    QUOTE_MIN_CHARS = 60
    QUOTE_MIN_WORDS = 8

    @classmethod
    def _normalize(cls, text):
        if not text:
            return ""
        return unicodedata.normalize("NFKD", text).encode("ascii", "ignore").decode("ascii")

    @classmethod
    def _normalize_movie_hyphen(cls, title):
        if not title:
            return title
        return re.sub(r'\s*[\u002D\u2010\u2011\u2012\u2013\u2014\u2015-]\s*(the movie\b)', r' the movie', title, flags=re.I)

    @classmethod
    def _adjust_base_title(cls, title):
        if not title or '"' not in title:
            return title
        words = title.split()
        if len(title) < cls.QUOTE_MIN_CHARS and len(words) < cls.QUOTE_MIN_WORDS:
            return title
        return title.split('"', 1)[0].strip()

    @classmethod
    def _clean_title(cls, title):
        if not title:
            return ""
        t = cls._normalize(title.lower())
        t = re.sub(r'\bassistir\s+online\b', '', t)
        t = re.sub(r'\bonline\b', '', t)
        t = re.sub(r'[\"""\'`]', '', t)
        t = re.sub(r'[-_:]', ' ', t)
        t = re.sub(r'[()\[\]]', '', t)
        t = re.sub(r'\b(\d+)(?:st|nd|rd|th|ª|º)\b', r'\1', t)
        t = re.sub(r'\s+', ' ', t)
        cleaned = t.strip()
        return cleaned

    @classmethod
    def _strip_dublado(cls, text):
        return re.sub(r'\bdublado\b', '', text).strip()

    @classmethod
    def _has_extra_words(cls, base_clean, cand_clean):
        return cls._strip_dublado(cand_clean) != base_clean

    @classmethod
    def _extract_year(cls, text):
        if not text:
            return None
        m = re.search(r'\b(19|20)\d{2}\b', text)
        return int(m.group()) if m else None

    @classmethod
    def _extract_season_number(cls, text):
        if not text:
            return None
        text_lower = text.lower()
        patterns = [
            r'\b(\d+)[ªº]?\s*(?:temporada|season|t|temp|s)\b',
            r'\b(temporada|season|t|temp|s)\s*(\d+)\b',
            r'\b(\d+)\s*(?:ª|º|st|nd|rd|th)?\s*(?:temporada|season)\b',
            r'\b(\d+)\b',
        ]
        season = None
        for pattern in patterns:
            m = re.search(pattern, text_lower)
            if m:
                try:
                    num = int(m.group(1))
                    if 1 <= num <= 20:
                        season = num
                        return season
                except:
                    pass
        return None

    @classmethod
    def _similarity_score(cls, base_titles, candidate_title, base_year=None, cand_year=None):
        cand_title = cls._normalize_movie_hyphen(candidate_title)
        cand_clean = cls._clean_title(cand_title)
        best_score = 0.0
        for base_title in base_titles:
            adj_base = cls._adjust_base_title(base_title)
            base_clean = cls._clean_title(adj_base)
            if not base_clean:
                continue
            if cls._has_extra_words(base_clean, cand_clean):
                continue
            score = difflib.SequenceMatcher(None, base_clean, cls._strip_dublado(cand_clean)).ratio()
            if 'dublado' in cand_clean:
                score += 0.25
            if base_year and cand_year:
                score += 0.5 if base_year == cand_year else -0.5
            if score > best_score:
                best_score = score
        return best_score

    @classmethod
    def _extract_episode_links_from_page(cls, page_text):
        soup = BeautifulSoup(page_text, 'html.parser')
        episodes = {}
        for item in soup.find_all('div', class_=re.compile(r'ultimos(?:Animes|Episodios)HomeItem')):
            ep_num = None
            num_div = item.find('div', class_='ultimosEpisodiosHomeItemInfosNum')
            if num_div:
                m = re.search(r'(?:episódio|ep|eps)\s*(\d+)', num_div.get_text(strip=True), re.I)
                if m:
                    ep_num = int(m.group(1))
            if ep_num is None:
                title_div = item.find('div', class_='ultimosAnimesHomeItemInfosNome')
                title_text = title_div.get_text(strip=True) if title_div else ""
                for pattern in [
                    r'(?:ep|eps|episódio)\s*(\d+)(?:\s|$|[^\d])',
                    r'episódio\s*(\d+)',
                    r'\b(\d{1,3})\b\s*(?:de\s*)?(?:episódio|ep|eps)?',
                    r'\b(\d+)\b',
                ]:
                    m = re.search(pattern, title_text, re.I)
                    if m:
                        ep_num = int(m.group(1))
                        break
            a = item.find('a', href=True)
            if a and '/videos/' in a['href'] and ep_num is not None:
                episodes[ep_num] = a['href']
        return episodes

    @classmethod
    def _build_page_url(cls, series_url, page_num):
        base = series_url.rstrip('/')
        return base if page_num <= 1 else f"{base}/page/{page_num}"

    @classmethod
    def _get_episode_page_url(cls, series_url, episode_num):
        page_num = 1
        while True:
            page_url = cls._build_page_url(series_url, page_num)
            r = session.get(page_url)
            if not r.ok:
                break
            episodes = cls._extract_episode_links_from_page(r.text)
            if not episodes:
                break
            if episode_num in episodes:
                return urljoin("https://www.hinatasoulbr.vip/", episodes[episode_num])
            page_num += 1
        r = session.get(series_url)
        if r.ok:
            episodes = cls._extract_episode_links_from_page(r.text)
            if episodes:
                return urljoin("https://www.hinatasoulbr.vip/", episodes[min(episodes.keys())])
        return None

    @classmethod
    def _get_movie_episode_url(cls, page_text):
        soup = BeautifulSoup(page_text, "html.parser")
        for item in soup.find_all('div', class_=re.compile(r'ultimos(?:Animes|Episodios)HomeItem')):
            a = item.find('a', href=True)
            if a and '/videos/' in a['href']:
                return urljoin("https://www.hinatasoulbr.vip/", a["href"])
        return None

    @classmethod
    def _get_available_qualities(cls, episode_page_text):
        soup = BeautifulSoup(episode_page_text, 'html.parser')
        abas_box = soup.find('div', class_=re.compile(r'AbasBox', re.I))
        if not abas_box:
            return ["SD"]
        available = []
        for aba in abas_box.find_all('div', class_=re.compile(r'Aba', re.I)):
            text = aba.get_text(strip=True).upper()
            if text in ("SD", "HD"):
                available.append(text)
            elif text in ("FULLHD", "FULL HD", "FHD"):
                available.append("FULLHD")
        return available if available else ["SD"]

    @classmethod
    def _extract_highest_quality_token(cls, episode_page_text):
        soup = BeautifulSoup(episode_page_text, 'html.parser')
        abas_box = soup.find('div', class_=re.compile(r'AbasBox', re.I))
        if not abas_box:
            return None, None
        available_qualities = {}
        for aba in abas_box.find_all('div', class_=re.compile(r'Aba', re.I)):
            text = aba.get_text(strip=True).upper()
            identifier = aba.get('aba-type') or aba.get('data-target') or str(len(available_qualities))
            if text in ("SD", "HD", "FULLHD", "FULL HD", "FHD"):
                norm = "FULLHD" if text in ("FULLHD", "FULL HD", "FHD") else text
                available_qualities[norm] = identifier
        if not available_qualities:
            return None, None
        selected = next(
            (q for q in ("FULLHD", "HD", "SD") if q in available_qualities),
            next(iter(available_qualities))
        )
        containers = soup.find_all('div', class_='playerContainer')
        if not containers:
            return None, None
        idx = int(available_qualities[selected])
        container = containers[idx] if idx < len(containers) else next(
            (c for c in containers if 'filezt.php?t=' in str(c)), None
        )
        if not container:
            return None, None
        a_tag = BeautifulSoup(str(container), 'html.parser').find(
            'a', href=re.compile(r'https://foodiesbrazil\.info/filezt\.php\?t='))
        if not a_tag or '?t=' not in a_tag['href']:
            return None, None
        token = a_tag['href'].split('?t=', 1)[1].strip()
        return selected, token

    @classmethod
    def _extract_url_from_iframe(cls, src):
        params = parse_qs(urlparse(src).query)
        if 'url' in params:
            return params['url'][0]
        m = re.search(r'[?&]url=([^&"\s]+)', src)
        return unquote(m.group(1)) if m else None

    @classmethod
    def _scan_iframes(cls, html, base_url):
        soup = BeautifulSoup(html, 'html.parser')
        iframes = soup.find_all('iframe', src=True)
        for iframe in iframes:
            src = iframe['src']
            if 'playerricas' in src.lower():
                url = cls._extract_url_from_iframe(src)
                if url:
                    return url
        for iframe in iframes:
            src = iframe['src']
            url = cls._extract_url_from_iframe(src)
            if url and any(x in url.lower() for x in ('.mp4', '.m3u8')):
                return url
        return None

    @classmethod
    def _extract_meta_refresh(cls, html, base_url):
        soup = BeautifulSoup(html, 'html.parser')
        meta = soup.find('meta', attrs={'http-equiv': re.compile(r'refresh', re.I)})
        content = meta.get('content', '') if meta else ''
        if not content:
            m = re.search(
                r'http-equiv=["\']?refresh["\']?[^>]*content=["\']?[^;]*;\s*url=([^\s"\'<>]+)',
                html, re.I)
            content = m.group(0) if m else ''
        m = re.search(r'url=([^\s"\'>;]+)', content, re.I)
        if not m:
            return None
        url = m.group(1).strip().strip('"\'')
        return urljoin(base_url, url) if not url.startswith('http') else url

    @classmethod
    def _get_direct_mp4_from_token_302(cls, token):
        if not token:
            return None
        current_url = f"https://foodiesbrazil.info/filezt.php?t={token}"
        prev_url = current_url
        try:
            for _ in range(10):
                r = session.get(current_url, allow_redirects=False, timeout=25)
                html = r.text

                found = cls._scan_iframes(html, current_url)
                if found:
                    return found

                final_url = r.headers.get('Location', '')
                if any(x in final_url.lower() for x in ('.mp4', '.m3u8')):
                    return final_url
                if any(x in current_url.lower() for x in ('.mp4', '.m3u8')):
                    return current_url

                next_url = final_url or cls._extract_meta_refresh(html, current_url)
                if not next_url:
                    break
                if not next_url.startswith('http'):
                    next_url = urljoin(current_url, next_url)

                session.headers.update({'Referer': prev_url})
                prev_url = current_url
                current_url = next_url

            return None
        except Exception:
            return None

    @classmethod
    def _get_highest_quality_link(cls, episode_page_text, available):
        quality, token = cls._extract_highest_quality_token(episode_page_text)
        if not token:
            return "HINATASOUL - SD", None
        mp4_url = cls._get_direct_mp4_from_token_302(token)
        return "HINATASOUL -", mp4_url or None

    @classmethod
    def search_animes(cls, mal_id, episode, anime_name, original_name, year=None):
        is_movie = episode is None
        if not is_movie:
            try:
                episode = int(episode)
            except:
                return []

        base_titles_raw = [t for t in [anime_name, original_name] if t]
        if not base_titles_raw:
            return []
        base_titles = [cls._adjust_base_title(t) for t in base_titles_raw]
        base_year = year

        items = []
        for search_title in base_titles:
            url = f"https://www.hinatasoulbr.vip/busca?busca={quote_plus(search_title)}"
            r = session.get(url)
            if not r.ok:
                continue
            soup = BeautifulSoup(r.text, "html.parser")
            items = [
                i for i in soup.find_all('div', class_=re.compile(r'ultimosAnimesHomeItem'))
                if i.find('div', class_='ultimosAnimesHomeItemInfosNome')
                and i.find('a', href=True)
                and re.search(r"/(animes|anime-dublado)/[^/]+$", i.find('a', href=True)['href'])
            ]
            if items:
                break

        if not items:
            return []

        candidates = []
        for item in items:
            name_div = item.find('div', class_='ultimosAnimesHomeItemInfosNome')
            raw_title = name_div.get_text(strip=True)
            a = item.find('a', href=True)
            page_url = urljoin("https://www.hinatasoulbr.vip/", a["href"])
            cand_year = cls._extract_year(raw_title)
            score = cls._similarity_score(base_titles, raw_title, base_year, cand_year)
            candidates.append({
                "title": raw_title,
                "url": page_url,
                "score": score,
                "year": cand_year,
                "season": cls._extract_season_number(raw_title),
                "clean_title": cls._clean_title(raw_title)
            })

        candidates.sort(key=lambda x: x["score"], reverse=True)

        expected_season = None
        if not is_movie:
            for t in base_titles:
                m = re.search(r'(?:season|part|temporada|s)\s*(\d+)', t.lower())
                if m:
                    expected_season = int(m.group(1))
                    break

        base_keywords = set()
        for t in base_titles:
            words = cls._clean_title(t).split()
            if len(words) > 1:
                base_keywords.update(words[:3])

        results = []
        seen = set()

        for c in candidates:
            accept = False
            if base_year and c["year"] and base_year == c["year"]:
                accept = True
            elif not is_movie and expected_season and c["season"] == expected_season:
                accept = True
            elif not is_movie and expected_season and c["season"] == expected_season:
                if any(kw in c["clean_title"] for kw in base_keywords) and str(expected_season) in c["clean_title"]:
                    accept = True
            if not accept:
                min_score = 0.75 if 'dublado' in c["title"].lower() else 0.55
                if c["score"] >= min_score:
                    accept = True
            if not accept or c["url"] in seen:
                continue
            seen.add(c["url"])

            r_page = session.get(c["url"])
            if not r_page.ok:
                continue

            ep_url = (
                cls._get_movie_episode_url(r_page.text)
                if is_movie
                else cls._get_episode_page_url(c["url"], episode)
            )
            if not ep_url:
                continue

            r_ep = session.get(ep_url)
            if not r_ep.ok:
                continue

            available = cls._get_available_qualities(r_ep.text)
            label, url = cls._get_highest_quality_link(r_ep.text, available)
            if not url:
                continue

            prefix = DUBBED if "dublado" in c["title"].lower() else SUBTITLED
            results.append((f"{label} {prefix}", url))

        return results

    @classmethod
    def resolve_animes(cls, url):
        resolved, sub = Resolver().resolverurls(url)
        return [(resolved or url, sub or '', USER_AGENT)]

    resolve_animes_movies = resolve_animes
    __site_url__ = ['https://www.hinatasoulbr.vip/']
