import re
import subprocess
import time
import requests
import os
import json
import shutil
import random
import urllib.parse
from imdb import IMDb
from celery import Celery
from elasticsearch import Elasticsearch
from base.utils import MovieoDatabase
from config import ENCODERS, RELEASE_TYPES, MOVIEO_TELEGRAM_API, DOWNLOAD_DIR, MOVIEO_SERVER_IP, ES_AUTH, ES_HOST, QUALITIES_PYRAMID, AUTO_SUB_CH, ARCHIVE_CHANNEL, MOVIEO_CLI


def identify_release(file_name):
    file_name_lower = file_name.lower()
    release_value = ''

    for key, value in RELEASE_TYPES.items():
        if key in file_name_lower:
            release_value = value
            break

    return release_value


def identify_seasons_from_irregular_filename(file_name: str):
    normalized = (
        file_name.lower()
        .replace("-", ".")
        .replace("_", ".")
        .replace("×", "x")
        .replace("–", ".")
        .replace(" ", ".")
    )

    season_patterns = [
        r"[Ss](\d{1,2})",
        r"[Ss]eason[._\s-]?(\d{1,2})",
    ]

    se = None
    for pattern in season_patterns:
        match = re.search(pattern, normalized, re.IGNORECASE)
        if match:
            se = int(match.group(1))
            break

    if not se:
        se = 1

    return se


def identify_episodes_from_irregular_filename(filename: str):
    name = (
        filename.lower()
        .replace("_", " ")
        .replace("-", " ")
        .replace(".", " ")
        .replace("×", "x")
        .replace("–", " ")
        .replace("—", " ")
    )

    name = re.sub(r"\b(720p|1080p|2160p|480p|4k|x265|hdr|webrip|web|bluray|bdrip|hdtv|dvdrip|remux)\b", "", name)
    name = re.sub(r"\b(19|20)\d{2}\b", "", name)
    name = re.sub(r"\s+", " ", name).strip()

    patterns = [
        r"[Ss]\d{1,2}[Ee](\d{1,3})",
        r"episode\s*(\d{1,3})",
        r"\bep\s*(\d{1,3})\b",
        r"\bep(\d{1,3})\b",
        r"(\d{1,2})x(\d{1,2})",
        r"[Ss]\d{1,2}[\s._-]*(\d{1,3})",
        r"^(\d{1,2})(?!\d)",
        r"[\s._-](\d{1,2})[\s._-]",
    ]

    episode = None

    for pattern in patterns:
        m = re.search(pattern, name, re.IGNORECASE)
        if m:
            groups = m.groups()
            if len(groups) == 2:
                ep = groups[1]
            else:
                ep = groups[0]
            ep = ep.lstrip("0")
            if ep.isdigit():
                ep = int(ep)
                if 1 <= ep <= 60:
                    episode = ep
                    break

    return episode

def identify_seasons(file_name: str):
    season = re.findall('[Ss]\d\d', file_name)
    if not season:
        se = None
    else:
        se = season[0]
        se = int(str(se).replace('S0', '').replace('s0', '').replace('S', '').replace('s', ''))
    return se

def identify_episodes(file_name: str):
    episode = re.findall('[Ee]\d\d\d\d', file_name)
    ep_s: str = ''
    episodes: str = ''
    permission = True
    if not episode:
        episode = re.findall('[Ee]\d\d\d', file_name)
        if not episode:
            episode = re.findall('[Ee]\d\d', file_name)
            if not episode:
                permission = False
            else:
                for ep in episode:
                    episodes += ep
                    ep = str(ep).replace('E0', '').replace('e0', '').replace('E', '').replace('e', '')
                    ep_s += ep + ','
        else:
            for ep in episode:
                episodes += ep
                ep = str(ep).replace('E00', '').replace('e00', '').replace('E0', '').replace('e0', '').replace('E','').replace('e', '')
                ep_s += ep + ','
    else:
        for ep in episode:
            episodes += ep
            ep = str(ep).replace('E000', '').replace('e000', '').replace('E00', '').replace('e00', '').replace('E0','').replace('e0', '').replace('E', '').replace('e', '')
            ep_s += ep + ','
    return permission,episodes,ep_s.split(',')[len(ep_s.split(','))-2]

def identify_quality(file_name: str):
    permission = True
    quality = re.findall('\d\d\d\d[Pp]', file_name)
    if not quality:
        quality = re.findall('\d\d\d[Pp]', file_name)
        if not quality:
            permission = False
        else:
            quality = str(quality[0])
            quality = quality.lower()
    else:
        quality = str(quality[0])
        quality = quality.lower()
    if permission:
        codec = re.findall('[Xx]265', file_name)
        if codec:
            codec = codec[0]
            quality = f'{quality}{codec}'
    return permission,quality

def identify_encoder_and_release(file_name):
    file_name_lower = file_name.lower()
    release_value = ''
    encoder_value = ''

    for key, value in ENCODERS.items():
        if key in file_name_lower:
            encoder_value = value
            break

    for key, value in RELEASE_TYPES.items():
        if key in file_name_lower:
            release_value = value
            break

    return release_value,encoder_value

def log_request(request_id: int, status: int):
    current_time = int(time.time())
    with MovieoDatabase() as db:
        users_requests_db = db.get_users_requests('id', request_id, 'chat_id,title_id')
        db.update_admins_requests(request_id, 'response_time', current_time)
        db.update_admins_requests(request_id, 'status', status)
        db.update_users_requests(request_id, 'status', status)
    chat_id = users_requests_db[0][0]
    title_id = users_requests_db[0][1]
    with MovieoDatabase() as db:
        title_db = db.get_title('id', title_id, 'movieo_id,en_name')
    if not title_db:
        if status == 3:
            ia = IMDb()
            title = ia.get_movie(int(title_id.replace('tt','')))
            movieo_id = None
            en_name = f'{title["title"]} {title["year"]}'
            return chat_id, movieo_id, en_name
        else:
            with MovieoDatabase() as db:
                title_db = db.get_title('imdb_id', title_id, 'movieo_id,en_name')
    return chat_id,title_db[0][0],title_db[0][1]

def concat_ws(sep, *args):
  return sep.join(str(a) for a in args if a not in (None, 'NULL'))

def run_command(command, error_message):
    print(f"--- executing command: {command}")
    result = subprocess.run(command, shell=True, capture_output=True, text=True)
    if result.returncode != 0:
        print(f"--- Error executing command: {error_message}")
        print(f"--- STDERR: {result.stderr}")
        return False
    return True

def get_video_duration_seconds(video_path: str):
    cmd = [
        "ffprobe",
        "-v", "error",
        "-show_entries", "format=duration",
        "-of", "json",
        video_path
    ]

    result = subprocess.run(
        cmd,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        text=True,
        check=True
    )

    data = json.loads(result.stdout)
    return float(data["format"]["duration"])

def get_random_second(sec: float):
    sec = int(sec)

    if sec <= 120:
        return 0

    start = 300 if sec > 600 else 0
    end = sec - 300 if sec > 600 else sec - 60

    return random.randint(start, end)

def send_request(url, method, data):
    if method == 'getFile':
        response = requests.post(f'{url}{method}', json=data, timeout=900)
    else:
        response = requests.post(f'{url}{method}', json=data)
    return response


def upload_video(url, method, data, files, file_name):
    MAX_RETRIES = 20
    RETRY_DELAY = 10

    for attempt in range(1, MAX_RETRIES + 1):

        try:
            response = requests.post(
                f'{url}{method}',
                data=data,
                files=files,
                timeout=3600
            )

            try:
                res_json = response.json()
            except ValueError:
                time.sleep(RETRY_DELAY)
                continue

            if not res_json.get('ok', False):
                time.sleep(RETRY_DELAY)
                continue

            return res_json

        except requests.exceptions.RequestException as e:
            time.sleep(RETRY_DELAY)
            continue

        except Exception as e:
            time.sleep(RETRY_DELAY)
            continue

    data = {
        'chat_id': 689423806,
        'text': f"Failed to upload {file_name} after {MAX_RETRIES} attempts. {response.text}",
    }
    send_request(MOVIEO_TELEGRAM_API, 'sendMessage', data)

def download_video(url, file_id, file_name):
    MAX_RETRIES = 50
    RETRY_DELAY = 1

    for attempt in range(1, MAX_RETRIES + 1):
        data = {
            'file_id': file_id,
        }
        response = send_request(url, 'getFile', data)
        if response.status_code == 200:
            json_data = json.loads(response.content)
            save_path = json_data['result']['file_path']
            _, file_format = os.path.splitext(f'{file_name}')
            server_file_name = f'{file_id}{file_format}'
            shutil.move(save_path, f'{DOWNLOAD_DIR}{server_file_name}')
            return server_file_name,file_format
        else:
            time.sleep(RETRY_DELAY)
    data = {
        'chat_id': 689423806,
        'text': f"Failed to download {file_name} after {MAX_RETRIES} attempts. {response.text}",
    }
    send_request(MOVIEO_TELEGRAM_API, 'sendMessage', data)
    return False

def convert_to_mkv(server_file_name: str, file_format: str):
    command = f'ffmpeg -y -fflags +genpts -i "{DOWNLOAD_DIR}{server_file_name}"  -map 0 -c copy -copy_unknown -ignore_unknown -max_interleave_delta 0 "{DOWNLOAD_DIR}{server_file_name.replace(file_format, ".mkv")}"'
    run_command(command, 'something went wrong when trying convert video to mkv.')
    os.remove(f"{DOWNLOAD_DIR}{server_file_name}")
    server_file_name = server_file_name.replace(file_format, ".mkv")
    return server_file_name

def get_all_request_info(request_id: int):
    with MovieoDatabase() as db:
        admin_request_db = db.get_admins_requests('id', request_id, 'admin_id')
        user_request_db = db.get_users_requests('id',request_id,'title_id,request_type,season')
    admin_user_id = admin_request_db[0][0]
    title_id = user_request_db[0][0]
    request_type_str = user_request_db[0][1]
    season = user_request_db[0][2]
    if request_type_str != 'title request':
        with MovieoDatabase() as db:
            title_db = db.get_title('id',title_id,'imdb_id,en_name,type')
        imdb_id = title_db[0][0]
        title_name = title_db[0][1]
        title_type = title_db[0][2]
    else:
        imdb_id = title_id
        title_id = None
        title_type = None
        title_name = None

    return admin_user_id, request_type_str, season, title_id, imdb_id, title_name, title_type

def fetch_title_metadata(imdb_id: str, title_type):
    num_digits = random.randint(4, 9)
    movieo_id = random.randint(10 ** (num_digits - 1), 10 ** num_digits - 1)
    with MovieoDatabase() as db:
        title_id = db.add_title(imdb_id)
        db.update_title(title_id, 'movieo_id', f'mo{movieo_id}')
        db.update_title(title_id, 'type', title_type)
    celery_app = Celery(
        "movieo_tasks",
        broker=f'amqp://admin:13990909_MVO@{MOVIEO_SERVER_IP}:5672//',
        backend=None,
    )
    celery_app.send_task("celery_tasks.tasks.get_title_info.fetch_title_metadata", args=[imdb_id, title_id, title_type])
    celery_app.send_task("celery_tasks.tasks.get_title_info.fetch_title_trailer", args=[imdb_id, title_id])
    celery_app.send_task("celery_tasks.tasks.get_title_info.fetch_imdb_rating", args=[imdb_id, title_id])
    celery_app.send_task("celery_tasks.tasks.get_title_info.fetch_parental_guide", args=[imdb_id, title_id])
    celery_app.send_task("celery_tasks.tasks.get_title_info.fetch_persian_plot", args=[imdb_id, title_id])
    celery_app.send_task("celery_tasks.tasks.get_title_info.fetch_title_actors", args=[imdb_id])
    celery_app.send_task("celery_tasks.tasks.get_title_info.fetch_title_directors", args=[imdb_id])
    if title_type == 1:
        celery_app.send_task("celery_tasks.tasks.get_title_info.fetch_movie_metascore", args=[imdb_id, title_id])
        celery_app.send_task("celery_tasks.tasks.get_title_info.fetch_movie_tomatometer", args=[imdb_id, title_id])
    else:
        celery_app.send_task("celery_tasks.tasks.get_title_info.fetch_series_metascore", args=[imdb_id, title_id])
        celery_app.send_task("celery_tasks.tasks.get_title_info.fetch_series_tomatometer", args=[imdb_id, title_id])
    time.sleep(30)
    with MovieoDatabase() as db:
        title_db = db.get_title('id', title_id, 'en_name')
    en_name = title_db[0][0]
    return title_id, en_name

def add_title_to_elasticsearch(title_id: int, en_name: str, title_type: int):
    es = Elasticsearch(hosts=ES_HOST, basic_auth=ES_AUTH, verify_certs=False)
    doc = {
        'title_id': title_id,
        'en_name': en_name,
        'fa_name': '',
        'type': title_type
    }
    es.index(index="titles", body=doc)

def finding_best_quality_for_convert(title_id: int, requested_quality: str, requested_season: int):
    files_db = None
    for quality in QUALITIES_PYRAMID.keys():
        if requested_quality.strip() in QUALITIES_PYRAMID[quality]:
            if requested_season != 100:
                with MovieoDatabase() as db:
                    files_db = db.get_files(
                        'title_id',
                        title_id,
                        'id,archive_id',
                        f'''AND quality = "{quality}" 
                             AND type = 1 
                             AND season = {requested_season} 
                             AND (
                                 (encoder = (SELECT encoder FROM files WHERE title_id = {title_id} AND type = 1 AND season = {requested_season} AND quality = "{quality}" LIMIT 1)
                                  OR (encoder IS NULL AND (SELECT encoder FROM files WHERE title_id = {title_id} AND type = 1 AND season = {requested_season} AND quality = "{quality}" LIMIT 1) IS NULL))
                                 AND
                                 (release_type = (SELECT release_type FROM files WHERE title_id = {title_id} AND type = 1 AND season = {requested_season} AND quality = "{quality}" LIMIT 1)
                                  OR (release_type IS NULL AND (SELECT release_type FROM files WHERE title_id = {title_id} AND type = 1 AND season = {requested_season} AND quality = "{quality}" LIMIT 1) IS NULL))
                             )'''
                    )
            else:
                with MovieoDatabase() as db:
                    files_db = db.get_files(
                        'title_id',
                        title_id,
                        'id,archive_id',
                        f'''AND quality = "{quality}" 
                             AND type = 1 
                             AND (
                                 (encoder = (SELECT encoder FROM files WHERE title_id = {title_id} AND type = 1 AND quality = "{quality}" LIMIT 1)
                                  OR (encoder IS NULL AND (SELECT encoder FROM files WHERE title_id = {title_id} AND type = 1 AND quality = "{quality}" LIMIT 1) IS NULL))
                                 AND
                                 (release_type = (SELECT release_type FROM files WHERE title_id = {title_id} AND type = 1 AND quality = "{quality}" LIMIT 1)
                                  OR (release_type IS NULL AND (SELECT release_type FROM files WHERE title_id = {title_id} AND type = 1 AND quality = "{quality}" LIMIT 1) IS NULL))
                             )'''
                    )
            if files_db:
                return files_db
    return files_db

def resolve_2_part_request(request_id: int, request_type_str: str, qu: str, combo_hash: str, ):
    request_type_str = request_type_str.split('+')[1].strip()
    with MovieoDatabase() as db:
        db.update_users_requests(request_id, 'files', qu)
        db.update_users_requests(request_id, 'request_type', request_type_str)
        db.update_admins_requests(request_id, 'request_type', request_type_str)
        db.update_admins_requests(request_id, 'source_file', None)
    if request_type_str in ['Persian SoftSub', 'English SoftSub', 'Persian HardSub', 'English HardSub']:
        with MovieoDatabase() as db:
            files_db = db.file_db = db.get_files('combo_hash_sha256', combo_hash, 'archive_id',f'ORDER BY `files`.`ep` ASC')
        if files_db:
            from celery_tasks.tasks.batch_sender import send_batch_message
            for f in files_db:
                data = {
                    'chat_id': AUTO_SUB_CH,
                    'from_chat_id': ARCHIVE_CHANNEL,
                    'message_id': f[0],
                    'caption': f'{request_type_str},{request_id}',
                }
                send_batch_message.delay(MOVIEO_TELEGRAM_API, 'copyMessage', data)
    else:
        file_db = db.get_files('combo_hash_sha256', combo_hash, 'file_id', f'ORDER BY RAND() LIMIT 1')
        file = file_db[0][0]
        caption = f'compress_video,{request_id},{qu}'
        data = {
            'chat_id': MOVIEO_CLI,
            'document': file,
            'caption': caption
        }
        send_request(MOVIEO_TELEGRAM_API, 'sendDocument', data)

def create_new_file_name(title_id: int, movieo_file_id: int, title_type: int, requested_quality: str):
    with MovieoDatabase() as db:
        files_db = db.get_files('id', movieo_file_id, 'release_type,season,ep')
        title_db = db.get_title('id', title_id, 'en_name,year')
    release_type = files_db[0][0]
    if release_type == None:
        release_type = ''
    else:
        release_type = f'.{release_type}'
    season = files_db[0][1]
    ep = files_db[0][2]
    full_en_name = title_db[0][0]
    year = title_db[0][1]
    en_name = full_en_name.split('(')[0]
    en_name = en_name.split(':')[0]
    en_name = en_name.replace('.', '')
    en_name = en_name.replace("'", "")
    en_name = en_name.replace(f'{year}', '').strip()
    file_en_name = ''
    if len(en_name.split(' ')) > 5:
        for name in en_name.strip().split(' '):
            file_en_name += f'{name[0].capitalize()}.'
        if title_type == 1:
            file_en_name = f'{file_en_name}{year}'
    else:
        file_en_name = en_name.replace(' ', '.')
        if title_type == 1:
            file_en_name = f'{file_en_name}.{year}'
    if title_type == 1:
        new_file_name = f'{file_en_name}.{requested_quality}{release_type}.MVO.mkv'
        btn = f'📥 {requested_quality}{release_type.replace(".", " ")} MVO'
        btn = urllib.parse.quote(btn)
    else:
        if season < 10:
            if ep < 10:
                new_file_name = f'{file_en_name}.S0{season}E0{ep}.{requested_quality}{release_type}.MVO.mkv'
                btn = urllib.parse.quote(f'📥 {full_en_name.split("(")[0]}S0{season}E0{ep}')
            else:
                new_file_name = f'{file_en_name}.S0{season}E{ep}.{requested_quality}{release_type}.MVO.mkv'
                btn = urllib.parse.quote(f'📥 {full_en_name.split("(")[0]}S0{season}E{ep}')
        else:
            if ep < 10:
                new_file_name = f'{file_en_name}.S{season}E0{ep}.{requested_quality}{release_type}.MVO.mkv'
                btn = urllib.parse.quote(f'📥 {full_en_name.split("(")[0]}S{season}E0{ep}')
            else:
                new_file_name = f'{file_en_name}.S{season}E{ep}.{requested_quality}{release_type}.MVO.mkv'
                btn = urllib.parse.quote(f'📥 {full_en_name.split("(")[0]}S{season}E{ep}')
    return new_file_name, btn, release_type.strip().replace('.',''), season, ep

def determine_encoding_settings(requested_quality: str):
    if '360p' in requested_quality:
        bit_rate = '360k'
        height = 640
        width = 360
    elif '480p' in requested_quality:
        bit_rate = '480k'
        height = 854
        width = 480
    elif '540p' in requested_quality:
        bit_rate = '540k'
        height = 960
        width = 540
    elif '720p' in requested_quality:
        bit_rate = '720k'
        height = 1280
        width = 720
    elif '1080p' in requested_quality:
        bit_rate = '1080k'
        height = 1920
        width = 1080

    if 'x265' in requested_quality:
        codec = 'libx265'
    else:
        codec = 'libx264'

    return bit_rate, height, width, codec

