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

import os
import sys
import re
import zipfile
import shutil
from difflib import SequenceMatcher

PY3 = sys.version_info[0] == 3

try:
    from kodi_six import xbmc, xbmcgui, xbmcplugin, xbmcaddon, xbmcvfs
    try:
        from kodi_six.xbmcvfs import translatePath
    except (ImportError, AttributeError):
        from kodi_six.xbmc import translatePath
except:
    pass

import requests
from bs4 import BeautifulSoup

if PY3:
    from urllib.parse import urlencode, quote, unquote_plus, parse_qs
else:
    from urllib import urlencode, quote, unquote_plus
    from urlparse import parse_qs

try:
    plugin = sys.argv[0]
    handle = int(sys.argv[1])
    __addon__ = xbmcaddon.Addon()
    __addonname__ = __addon__.getAddonInfo('name')
    __profile__ = translatePath(__addon__.getAddonInfo('profile'))
    __temp__ = translatePath(os.path.join(__profile__, 'temp', ''))
except:
    __addonname__ = 'Legendei'
    __temp__ = '/tmp/legendei/'

__headers__ = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36'
}
__website__ = 'https://legendei.net/'

if not os.path.exists(__temp__):
    try:
        os.makedirs(__temp__)
    except:
        pass


def get_info_from_imdb(imdb_id):
    if not imdb_id:
        return None, None, None
    
    url = f'https://m.imdb.com/pt/title/{imdb_id}/'
    headers = {
        'Accept-Language': 'pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7',
        'User-Agent': 'Mozilla/5.0 (Linux; Android 10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Mobile Safari/537.36'
    }
    
    try:
        r = requests.get(url, headers=headers, timeout=15)
        if not r or r.status_code != 200:
            return None, None, None
        
        soup = BeautifulSoup(r.text, 'html.parser')
        
        title_local = ''
        hero_span = soup.find('span', {'data-testid': 'hero__primary-text'})
        if hero_span:
            title_local = hero_span.get_text(strip=True)
        else:
            hero = soup.find('h1', {'data-testid': 'hero__pageTitle'})
            if hero:
                title_local = hero.get_text(strip=True)
        
        original_title = ''
        orig_block = soup.find('div', {'data-testid': 'hero-title-block__original-title'})
        if orig_block:
            txt = orig_block.get_text(strip=True)
            original_title = re.sub(r'^(T[íi]tulo original|Original title)[:\s]*', '', txt, flags=re.I).strip()
        
        if not original_title:
            orig_div = soup.find('div', string=re.compile(r'^T[íi]tulo original[:\s]*', re.I))
            if orig_div:
                txt = orig_div.get_text(strip=True)
                original_title = re.sub(r'^T[íi]tulo original[:\s]*', '', txt, flags=re.I).strip()
        
        if not original_title:
            m = re.search(r'T[íi]tulo original[:\s]*["\']?([^<"\']+)["\']?', r.text, re.I)
            if m:
                original_title = m.group(1).strip()
        
        if not original_title:
            original_title = title_local
        
        year = ''
        year_link = soup.find('a', href=re.compile(r'/releaseinfo'))
        if year_link:
            y = re.search(r'\d{4}', year_link.text)
            if y:
                year = y.group(0)
        
        if not year:
            release_li = soup.find('li', {'data-testid': 'title-details-releasedate'})
            if release_li:
                y = re.search(r'\d{4}', release_li.get_text())
                if y:
                    year = y.group(0)
        
        if not year:
            y = re.search(r'\b(19|20)\d{2}\b', r.text[:6000])
            if y:
                year = y.group(0)
        
        return original_title, title_local, year or None
        
    except:
        return None, None, None


def clean_search_term(term):
    if not term:
        return ''
    
    term = term.replace(':', ' ')
    term = term.replace('-', ' ')
    term = ' '.join(term.split())
    
    return term


class FileManager:
    
    @staticmethod
    def clear_temp():
        if not os.path.exists(__temp__):
            return
        
        for root, dirs, files in os.walk(__temp__):
            for file in files:
                if file.endswith(('.srt', '.url', '.rar', '.zip')):
                    try:
                        os.remove(os.path.join(root, file))
                    except:
                        pass
        
        for name in os.listdir(__temp__):
            try:
                path = os.path.join(__temp__, name)
                if os.path.isdir(path):
                    shutil.rmtree(path)
            except:
                pass
    
    @staticmethod
    def extract_archive(archive_path, dest_path):
        success = False
        
        if zipfile.is_zipfile(archive_path):
            try:
                with zipfile.ZipFile(archive_path, 'r') as zip_ref:
                    zip_ref.extractall(dest_path)
                success = True
            except:
                pass
        
        if not success:
            try:
                FileManager._extract_rar_vfs(archive_path, dest_path)
                success = True
            except:
                pass
        
        return success
    
    @staticmethod
    def _extract_rar_vfs(rar_path, dest_path):
        rar_path_quoted = quote(rar_path, safe='')
        root = f'rar://{rar_path_quoted}/'
        
        dirs, files = xbmcvfs.listdir(root)
        
        if dirs:
            for directory in dirs:
                src_dir = os.path.join(root, directory)
                dst_dir = os.path.join(dest_path, directory)
                os.makedirs(dst_dir, exist_ok=True)
                FileManager._extract_rar_recursive(src_dir, dst_dir)
        
        for file in files:
            src_file = os.path.join(root, file)
            dst_file = os.path.join(dest_path, file)
            xbmcvfs.copy(src_file, dst_file)
    
    @staticmethod
    def _extract_rar_recursive(src_path, dst_path):
        dirs, files = xbmcvfs.listdir(src_path)
        
        if dirs:
            for directory in dirs:
                src_dir = os.path.join(src_path, directory)
                dst_dir = os.path.join(dst_path, directory)
                os.makedirs(dst_dir, exist_ok=True)
                FileManager._extract_rar_recursive(src_dir, dst_dir)
        
        for file in files:
            src_file = os.path.join(src_path, file)
            dst_file = os.path.join(dst_path, file)
            xbmcvfs.copy(src_file, dst_file)
    
    @staticmethod
    def find_subtitles():
        subtitles = []
        
        for root, dirs, files in os.walk(__temp__):
            for file in files:
                if file.lower().endswith('.srt'):
                    full_path = os.path.join(root, file)
                    subtitles.append((file, full_path))
        
        return subtitles


class VideoDetector:
    
    @staticmethod
    def get_info():
        info = {
            'title': None,
            'original_name': None,
            'originaltitle': None,
            'season': None,
            'episode': None,
            'year': None,
            'imdb': None
        }
        
        log_info = VideoDetector._extract_from_log()
        
        has_log_title = bool(log_info.get('original_name') or log_info.get('title'))
        
        for key in ['original_name', 'title', 'season', 'episode', 'year']:
            if log_info.get(key):
                info[key] = log_info[key]
        
        try:
            tag = xbmc.Player().getVideoInfoTag()
            
            info['imdb'] = tag.getIMDBNumber() or None
            
            if not has_log_title and info['imdb']:
                imdb_original, imdb_localized, imdb_year = get_info_from_imdb(info['imdb'])
                
                if imdb_original:
                    info['original_name'] = imdb_original
                
                if imdb_localized and imdb_localized != imdb_original:
                    info['title'] = imdb_localized
                elif imdb_original and not info.get('title'):
                    info['title'] = imdb_original
                
                if imdb_year and not info['year']:
                    info['year'] = imdb_year
            
            if not info['season']:
                try:
                    season = tag.getSeason()
                    info['season'] = season if season > 0 else None
                except:
                    pass
            
            if not info['episode']:
                try:
                    episode = tag.getEpisode()
                    info['episode'] = episode if episode > 0 else None
                except:
                    pass
        except:
            pass
        
        return info
    
    @staticmethod
    def _extract_from_log():
        info = {}
        
        log_path = VideoDetector._get_log_path()
        if not log_path or not os.path.exists(log_path):
            return info
        
        try:
            with open(log_path, 'r', encoding='utf-8', errors='ignore') as f:
                lines = f.readlines()[-500:]
            
            for line in reversed(lines):
                if 'Legendei' in line:
                    continue
                
                if 'title=' in line or 'name=' in line:
                    VideoDetector._parse_log_line(line, info)
                    
                    if info.get('title') and (info.get('season') or info.get('year')):
                        break
        except:
            pass
        
        return info
    
    @staticmethod
    def _parse_log_line(line, info):
        try:
            match = re.search(r'original_name=([^&\'\)]+)', line)
            if match and not info.get('original_name'):
                original_name = unquote_plus(match.group(1)).strip()
                
                is_series = bool(re.search(r'season|episode|season_num|episode_num', line))
                
                if is_series and ' - ' in original_name:
                    original_name = original_name.split(' - ')[0].strip()
                
                if not is_series:
                    original_name = re.sub(r'\s*\(\d{4}\)$', '', original_name).strip()
                
                info['original_name'] = original_name
            
            match = re.search(r'originaltitle=([^&\'\)]+)', line)
            if match and not info.get('originaltitle'):
                originaltitle = unquote_plus(match.group(1)).strip()
                
                is_series = bool(re.search(r'season|episode|season_num|episode_num', line))
                
                if is_series and ' - ' in originaltitle:
                    originaltitle = originaltitle.split(' - ')[0].strip()
                
                if not is_series:
                    originaltitle = re.sub(r'\s*\(\d{4}\)$', '', originaltitle).strip()
                
                info['originaltitle'] = originaltitle
            
            if not info.get('title'):
                patterns = [
                    r'title=([^&\'\)]+)',
                    r'serie_name=([^&\'\)]+)',
                    r'movie&name=([^&\'\)]+)',
                    r'movie_name=([^&\'\)]+)',
                    r'name=([^&\'\)]+)'
                ]
                
                for pattern in patterns:
                    match = re.search(pattern, line)
                    if match:
                        title = unquote_plus(match.group(1)).strip()
                        
                        is_series = bool(re.search(r'season|episode|season_num|episode_num', line))
                        
                        if is_series and ' - ' in title:
                            title = title.split(' - ')[0].strip()
                        
                        if not is_series:
                            title = re.sub(r'\s*\(\d{4}\)$', '', title).strip()
                        
                        info['title'] = title
                        break
            
            for pattern in [r'season=(\d+)', r'season_num=(\d+)']:
                match = re.search(pattern, line)
                if match and not info.get('season'):
                    info['season'] = int(match.group(1))
            
            for pattern in [r'episode=(\d+)', r'episode_num=(\d+)']:
                match = re.search(pattern, line)
                if match and not info.get('episode'):
                    info['episode'] = int(match.group(1))
            
            match = re.search(r'year=(\d{4})', line)
            if match and not info.get('year'):
                info['year'] = match.group(1)
        except:
            pass
    
    @staticmethod
    def _get_log_path():
        paths = [
            'special://logpath/kodi.log',
            'special://temp/kodi.log',
            os.path.expanduser('~/.kodi/temp/kodi.log')
        ]
        
        for path in paths:
            try:
                if path.startswith('special://'):
                    path = translatePath(path)
                if os.path.exists(path):
                    return path
            except:
                pass
        
        return None


class SubtitleClient:
    
    @staticmethod
    def search(search_term, category=None):
        search_term = SubtitleClient._clean_search_term(search_term)
        
        search_encoded = search_term.replace(' ', '+')
        
        if category:
            url = f"{__website__}category/{category}/?s={search_encoded}"
        else:
            url = f"{__website__}?s={search_encoded}"
        
        url = url.replace('+TV', '').replace('+Series', '')
        
        html_content = SubtitleClient._fetch_all_pages(url)
        
        if not html_content:
            return []
        
        results = SubtitleClient._parse_results(html_content, search_term)
        
        return results
    
    @staticmethod
    def _fetch_all_pages(start_url):
        html_content = ''
        current_url = start_url
        page_count = 0
        max_pages = 2
        
        while current_url and page_count < max_pages:
            try:
                r = requests.get(current_url, headers=__headers__, timeout=10)
                
                if r.status_code != 200:
                    break
                
                html_content += r.text
                page_count += 1
                
                soup = BeautifulSoup(r.text, 'html.parser')
                next_link = soup.find('a', {'class': 'sfwppa-pages sfwppa-link sfwppa-link-next'})
                
                if next_link:
                    current_url = next_link.get('href')
                else:
                    break
                    
            except:
                break
        
        return html_content
    
    @staticmethod
    def _parse_results(html, original_search):
        results = []
        
        try:
            soup = BeautifulSoup(html, 'html.parser')
            spans = soup.find_all("span", style=re.compile(r"font-size:\s*14px"))
            
            for span in spans:
                a_tag = span.find('a')
                if not a_tag:
                    continue
                
                link = a_tag.get('href', '')
                name = a_tag.get('title', '')
                
                if not link or not name:
                    continue
                
                similarity = SubtitleClient._calculate_similarity(original_search, name)
                
                results.append({
                    'name': name,
                    'link': link,
                    'similarity': similarity
                })
            
            min_similarity = 0.4
            results = [r for r in results if r['similarity'] >= min_similarity]
            results.sort(key=lambda x: x['similarity'], reverse=True)
            
        except:
            pass
        
        return results
    
    @staticmethod
    def _calculate_similarity(search_term, result_name):
        search_clean = search_term
        year_match = re.search(r'\b(19|20)\d{2}\b', search_term)
        episode_match = re.search(r'\bS\d{1,2}E\d{1,2}\b', search_term, re.I)
        
        if episode_match:
            search_clean = search_term[:episode_match.start()].strip()
        elif year_match:
            search_clean = search_term[:year_match.start()].strip()
        
        result_clean = result_name
        year_match_result = re.search(r'\b(19|20)\d{2}\b', result_name)
        episode_match_result = re.search(r'\bS\d{1,2}E\d{1,2}\b', result_name, re.I)
        
        if episode_match_result:
            result_clean = result_name[:episode_match_result.start()].strip()
        elif year_match_result:
            result_clean = result_name[:year_match_result.start()].strip()
        
        search_clean = re.sub(r'[^\w\s]', ' ', search_clean.lower())
        result_clean = re.sub(r'[^\w\s]', ' ', result_clean.lower())
        
        search_clean = ' '.join(search_clean.split())
        result_clean = ' '.join(result_clean.split())
        
        if result_clean != search_clean:
            return 0.0
        
        return 1.0
    
    @staticmethod
    def _clean_search_term(term):
        if not term:
            return ''
        
        term = term.replace(':', ' ')
        term = term.replace('-', ' ')
        term = ' '.join(term.split())
        term = re.sub(r'\s*\(\d{4}[-–]?\d{0,4}\)\s*$', '', term)
        
        return term.strip()
    
    @staticmethod
    def download_subtitle(url):
        try:
            if 'dl_id=' in url:
                return SubtitleClient._download_direct_link(url)
            
            r = requests.get(url, headers=__headers__, allow_redirects=True, timeout=15)
            
            if r.status_code != 200:
                return False
            
            content_type = r.headers.get('Content-Type', '')
            
            if 'text/html' in content_type:
                download_url = SubtitleClient._extract_download_link(r.text)
                if download_url:
                    return SubtitleClient._download_file(download_url)
            else:
                return SubtitleClient._save_and_extract(r.content)
                
        except:
            pass
        
        return False
    
    @staticmethod
    def _download_direct_link(url):
        try:
            r = requests.get(url, headers=__headers__, allow_redirects=True, timeout=20)
            
            if r.status_code != 200:
                return False
            
            content_type = r.headers.get('Content-Type', '').lower().strip()
            disposition = r.headers.get('Content-Disposition', '').lower()
            
            is_direct_download = (
                'attachment' in disposition or
                'application' in content_type or
                'octet-stream' in content_type or
                'zip' in content_type or
                'rar' in content_type or
                'download' in content_type or
                (len(r.content) > 100 and r.content[:4] == b'PK\x03\x04')
            )
            
            if is_direct_download:
                return SubtitleClient._save_and_extract(r.content)
            else:
                download_url = SubtitleClient._extract_download_link(r.text)
                if download_url:
                    return SubtitleClient._download_file(download_url)
            
            return False
            
        except:
            return False
    
    @staticmethod
    def _extract_download_link(html):
        try:
            soup = BeautifulSoup(html, 'html.parser')
            
            button = soup.find('a', class_='buttonmyup')
            if button and button.get('href'):
                link = button['href']
                if not link.startswith('http'):
                    link = __website__.rstrip('/') + '/' + link.lstrip('/')
                return link
            
            post_attachment = soup.find('p', {'class': 'post-attachment'})
            if post_attachment:
                a_tag = post_attachment.find('a')
                if a_tag and a_tag.get('href'):
                    link = a_tag['href']
                    if not link.startswith('http'):
                        link = __website__.rstrip('/') + '/' + link.lstrip('/')
                    return link
        except:
            pass
        
        return None
    
    @staticmethod
    def _download_file(url):
        try:
            r = requests.get(url, headers=__headers__, timeout=15)
            
            if r.status_code == 200:
                return SubtitleClient._save_and_extract(r.content)
        except:
            pass
        
        return False
    
    @staticmethod
    def _save_and_extract(content):
        temp_file = os.path.join(__temp__, 'subtitle_package.tmp')
        
        try:
            with open(temp_file, 'wb') as f:
                f.write(content)
            
            success = FileManager.extract_archive(temp_file, __temp__)
            
            try:
                os.remove(temp_file)
            except:
                pass
            
            return success
            
        except:
            pass
        
        return False


def log(module, msg):
    try:
        xbmc.log(f"### [Legendei - {module}] - {msg}", level=xbmc.LOGDEBUG)
    except:
        pass


def add_subtitle_item(name, link, similarity):
    try:
        params = {
            'action': 'download',
            'sub_download': link
        }
        url = f"{plugin}?{urlencode(params)}"
        
        score = int(similarity * 5)
        rating = '5' if score >= 4 else str(score)
        
        li = xbmcgui.ListItem(label='Portuguese (Brasil)', label2=name)
        li.setArt({"icon": rating, "thumb": 'pb'})
        
        xbmcplugin.addDirectoryItem(
            handle=handle,
            url=url,
            listitem=li,
            isFolder=False
        )
    except:
        pass


def show_subtitle_selection(subtitles):
    if not subtitles:
        notify('Nenhuma legenda disponível')
        return
    
    names = [s[0] for s in subtitles]
    paths = [s[1] for s in subtitles]
    
    index = xbmcgui.Dialog().select('Selecionar legenda', names)
    
    if index >= 0:
        selected_path = paths[index]
        set_subtitle(selected_path)


def set_subtitle(subtitle_path):
    try:
        li = xbmcgui.ListItem(label2=os.path.basename(subtitle_path))
        xbmcplugin.addDirectoryItem(
            handle=handle,
            url=subtitle_path,
            listitem=li,
            isFolder=False
        )
        xbmcplugin.endOfDirectory(handle)
    except:
        pass


def notify(message, icon='INFO'):
    try:
        icon_map = {
            'INFO': xbmcgui.NOTIFICATION_INFO,
            'WARNING': xbmcgui.NOTIFICATION_WARNING,
            'ERROR': xbmcgui.NOTIFICATION_ERROR
        }
        xbmcgui.Dialog().notification(
            'Legendei',
            message,
            icon_map.get(icon, xbmcgui.NOTIFICATION_INFO),
            3000
        )
    except:
        pass


def search_subtitle(lang, manual_search=None):
    FileManager.clear_temp()
    
    if manual_search:
        results = SubtitleClient.search(manual_search)
        
        for result in results:
            add_subtitle_item(result['name'], result['link'], result['similarity'])
        
        try:
            xbmcplugin.endOfDirectory(handle)
        except:
            pass
        return
    
    video_info = VideoDetector.get_info()
    
    search_candidates = []
    
    if video_info.get('original_name'):
        search_candidates.append(('original_name', video_info['original_name']))
    
    if video_info.get('title'):
        search_candidates.append(('title', video_info['title']))
    
    if not search_candidates:
        notify('Informações do vídeo não encontradas', 'WARNING')
        return
    
    is_series = video_info.get('season') and video_info.get('episode')
    category = 'serie' if is_series else 'filmes'
    
    final_results = []
    
    for candidate_name, candidate_title in search_candidates:
        clean_title = clean_search_term(candidate_title)
        
        if is_series:
            season = str(video_info['season']).zfill(2)
            episode = str(video_info['episode']).zfill(2)
            search_term = f"{clean_title} S{season}E{episode}"
        else:
            search_term = clean_title
            year = video_info.get('year')
            if year and year not in ['', False, -1, '-1']:
                search_term += f" {year}"
        
        results = SubtitleClient.search(search_term, category)
        
        if results:
            final_results = results
            break
    
    if not final_results:
        notify('Nenhuma legenda encontrada', 'WARNING')
        try:
            xbmcplugin.endOfDirectory(handle)
        except:
            pass
        return
    
    for result in final_results[:20]:
        add_subtitle_item(result['name'], result['link'], result['similarity'])
    
    try:
        xbmcplugin.endOfDirectory(handle)
    except:
        pass


def download_subtitle(url):
    url = unquote_plus(url)
    
    FileManager.clear_temp()
    
    success = SubtitleClient.download_subtitle(url)
    
    if success:
        subtitles = FileManager.find_subtitles()
        
        if subtitles:
            show_subtitle_selection(subtitles)
        else:
            notify('Nenhuma legenda encontrada no arquivo', 'WARNING')
    else:
        notify('Erro ao baixar legenda', 'ERROR')


if __name__ == '__main__':
    try:
        params = dict(parse_qs(sys.argv[2][1:]))
        params = {k: v[0] if v else None for k, v in params.items()}
    except:
        params = {}
    
    action = params.get('action')
    
    if action == 'search':
        search_subtitle('pt', params.get('searchstring'))
    elif action == 'download':
        download_subtitle(params.get('sub_download', ''))
    else:
        search_subtitle('pt')