# ==============================================================
# downloader.py — הורדת סרטונים מקומית עם תור, זיהוי מהירות ושמע/וידאו
# פתרון משולב: חילוץ פלייליסט עם עוגיות (עוקף נטפרי) -> הורדת סרטונים כבודדים עם אנדרואיד (עוקף יוטיוב)
# ==============================================================

import os
import platform
import subprocess
import threading
import uuid
import re
import queue

from py.config import BASE_DIR

# מילון גלובלי לשמירת מצב ההורדות
active_downloads = {}

# תור לניהול משימות ההורדה
download_queue = queue.Queue()

def _ytdlp_exe():
    return os.path.join(BASE_DIR, 'yt-dlp.exe')

def _creationflags():
    return subprocess.CREATE_NO_WINDOW if platform.system() == 'Windows' else 0

def sanitize_filename(name):
    """ מסיר תווים שאסורים לשימוש בשמות קבצים ותיקיות בווינדוס """
    if not name:
        return "Unknown"
    return re.sub(r'[\\/*?:"<>|]', "", name).strip()

def start_download(video_id_or_url, category_name, channel_name, is_playlist=False, subfolder=None, file_prefix=None, requested_title=None):
    """ מוסיף את משימת ההורדה או החילוץ לתור """
    job_id = str(uuid.uuid4())
    is_custom = (channel_name == 'קישורים ידניים')
    
    active_downloads[job_id] = {
        "status": "queued",    
        "progress": 0.0,
        "speed": "",           
        "phase": "starting",   
        "video_id": video_id_or_url, 
        "category": category_name,
        "channel": channel_name,
        "is_playlist": is_playlist,
        "is_custom": is_custom,
        "process": None,
        "subfolder": subfolder,     # תיקיית משנה (משמש לפלייליסטים)
        "file_prefix": file_prefix  # קידומת לקובץ (למשל מספר הסרטון)
    }
    
    # הגדרת כותרת רלוונטית לממשק
    if requested_title:
        active_downloads[job_id]["title"] = requested_title
    elif is_playlist:
        active_downloads[job_id]["title"] = "פלייליסט בהורדה..."
    else:
        active_downloads[job_id]["title"] = "סרטון בהורדה..."
        
    download_queue.put((job_id, video_id_or_url, category_name, channel_name, is_playlist, subfolder, file_prefix))
    
    display_id = video_id_or_url[:25] + "..." if len(video_id_or_url) > 25 else video_id_or_url
    print(f"\n  [תור] משימה {job_id[:8]} ({display_id}) נוספה לתור.", flush=True)
    
    return job_id

def _download_worker():
    """ הפועל שרץ ברקע כל הזמן: שולף משימות מהתור ומבצע אותן אחת-אחת """
    while True:
        job_data = download_queue.get()
        if job_data is None:
            break 
            
        job_id, video_id, category_name, channel_name, is_playlist, subfolder, file_prefix = job_data
        
        if active_downloads.get(job_id, {}).get("status") == "canceled":
            print(f"  [תור] דילוג על משימה {job_id[:8]} (בוטלה על ידי המשתמש)", flush=True)
            download_queue.task_done()
            continue
            
        _process_download(job_id, video_id, category_name, channel_name, is_playlist, subfolder, file_prefix)
        download_queue.task_done()

threading.Thread(target=_download_worker, daemon=True).start()

def _process_download(job_id, video_id, category_name, channel_name, is_playlist, subfolder, file_prefix):
    """ הפונקציה המבצעת: מחלצת פלייליסטים או מורידה סרטונים בודדים """
    display_id = video_id[:25] + "..." if len(video_id) > 25 else video_id
    try:
        active_downloads[job_id]["status"] = "downloading"
        is_custom = active_downloads[job_id].get("is_custom", False)
        safe_category = sanitize_filename(category_name)
        
        if video_id.startswith('http://') or video_id.startswith('https://'):
            url = video_id
        else:
            url = f"https://www.youtube.com/watch?v={video_id}"
            
        if is_playlist:
            # =================================================================
            # שלב א': חילוץ פלייליסט בעזרת yt-dlp ועוגיות פיירפוקס (עובר נטפרי!)
            # =================================================================
            active_downloads[job_id]["phase"] = "starting"
            active_downloads[job_id]["title"] = "שולף סרטונים מהפלייליסט..."
            
            cmd = [
                _ytdlp_exe(),
                url,
                "--flat-playlist",  # מחלץ רק טקסט, לא מוריד כלום!
                "--print", "%(playlist_index)s|*|%(id)s|*|%(title)s|*|%(playlist_title)s", # מבנה קל לפענוח
                "--cookies-from-browser", "firefox", # חובה לשליפת הרשימה כדי לעבור חסימת נטפרי
                "--sleep-requests", "1",             # מונע חסימת עומס
                "--retries", "10", "--fragment-retries", "10",
                "--no-check-certificate",
                "--ignore-errors",
                "--user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36"
            ]
            
            print(f"\n  [הורדה - {display_id}] מתחיל חילוץ פלייליסט", flush=True)
            
            custom_env = os.environ.copy()
            custom_env["PYTHONIOENCODING"] = "utf-8"

            process = subprocess.Popen(
                cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
                env=custom_env, creationflags=_creationflags()
            )
            
            active_downloads[job_id]["process"] = process
            extracted_count = 0
            
            # קריאת הפלט (הרשימה ש-yt-dlp מדפיס)
            for line_bytes in iter(process.stdout.readline, b''):
                if not line_bytes: break
                try:
                    clean_line = line_bytes.decode('utf-8').strip()
                except UnicodeDecodeError:
                    clean_line = line_bytes.decode('mbcs', errors='replace').strip()
                    
                if not clean_line: continue
                
                # זיהוי שורת נתונים תקינה עם המפריד שלנו
                if "|*|" in clean_line:
                    parts = clean_line.split("|*|")
                    if len(parts) >= 4:
                        p_index = parts[0].strip()
                        v_id = parts[1].strip()
                        v_title = parts[2].strip()
                        p_title = parts[3].strip()
                        
                        if p_index == "NA": p_index = f"{extracted_count + 1:02d}"
                        if p_title == "NA": p_title = "Playlist"
                        
                        # הגדרת התיקייה בה יישמרו הסרטונים
                        target_subfolder = sanitize_filename(p_title)
                        if subfolder:
                            target_subfolder = f"{subfolder}/{target_subfolder}"
                            
                        # מוסיפים את הסרטון הבודד לתור הרגיל של השרת!
                        start_download(
                            video_id_or_url=v_id,
                            category_name=category_name,
                            channel_name=channel_name,
                            is_playlist=False, # חשוב מאוד! הסרטון הזה יורד כבודד (ללא עוגיות)
                            subfolder=target_subfolder,
                            file_prefix=f"{p_index} - ",
                            requested_title=f"{p_index} - {v_title}"
                        )
                        extracted_count += 1
                        active_downloads[job_id]["progress"] = min(99.0, extracted_count * 2.0)
                        print(f"  [פלייליסט] התווסף לתור: {p_index} - {v_title}", flush=True)
                else:
                    if "Blocked by NetFree" in clean_line or "HTTP Error 418" in clean_line:
                        print(f"  [חסימה - {display_id}] חסימה חלקית בנטפרי בשלב הקריאה", flush=True)
                    else:
                        print(f"  [yt-dlp: {display_id}] {clean_line[:150]}", flush=True)
                        
            process.stdout.close()
            process.wait()
            
            if extracted_count > 0:
                print(f"  [הורדה - {display_id}] סהכ {extracted_count} סרטונים חולצו והוכנסו לתור!", flush=True)
                active_downloads[job_id]["status"] = "done"
                active_downloads[job_id]["progress"] = 100.0
            else:
                print(f"  [הורדה - {display_id}] כשלון בחילוץ או פלייליסט ריק (ודא שהעמוד אושר בנטפרי בדפדפן פיירפוקס)", flush=True)
                active_downloads[job_id]["status"] = "error"
                
            return # סיימנו את תפקיד משימת החילוץ! המשימות החדשות יעשו את ההורדה.

        else:
            # =================================================================
            # שלב ב': מצב הורדת סרטון בודד (ללא עוגיות, עוקף חתימות יוטיוב)
            # =================================================================
            # הגדרת נתיב ההורדה לתיקיית ההורדות של המשתמש (Downloads) בווינדוס
            user_downloads = os.path.join(os.path.expanduser("~"), "Downloads")
            download_dir = os.path.join(user_downloads, "MyYouTube", safe_category)
            
            # ניהול חכם של תת-תיקיות כדי למנוע עומק מיותר:
            if subfolder:
                # 1. אם זה פלייליסט, התיקייה תהיה אך ורק שם הפלייליסט (רמה אחת בלבד)
                clean_sub = sanitize_filename(subfolder.replace('/', ' - '))
                download_dir = os.path.join(download_dir, clean_sub)
            elif not is_custom:
                # 2. אם זו הורדה רגילה מספריית הערוצים, התיקייה תהיה שם הערוץ
                safe_channel = sanitize_filename(channel_name)
                download_dir = os.path.join(download_dir, safe_channel)
                    
            os.makedirs(download_dir, exist_ok=True)
            
            # הוספת הקידומת (למשל "01 - ") לשם הקובץ אם קיים
            filename_template = f"{file_prefix}%(title)s.%(ext)s" if file_prefix else "%(title)s.%(ext)s"
            
            # 3. במצב מותאם אישית וללא פלייליסט (סרטון בודד), נשתמש בשם הערוץ המקורי כתיקייה
            if is_custom and not subfolder:
                output_template = os.path.join(download_dir, "%(uploader|Unknown)s", filename_template)
            else:
                output_template = os.path.join(download_dir, filename_template)

            cmd = [
                _ytdlp_exe(),
                url,
                "-f", "bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/18/best",
                "--newline", 
                "--no-check-certificate", 
                "--ignore-errors",
                # טריק האנדרואיד המושלם (ללא עוגיות) פועל כאן בביטחה לכל סרטון:
                "--extractor-args", "youtube:player_client=android,web",
                "--user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36",
                "--no-playlist",
                "-o", output_template
            ]
            
            print(f"\n  [הורדה - {display_id}] מתחיל משימת הורדה", flush=True)
            print(f"  [הורדה - {display_id}] יעד: {download_dir}", flush=True)
            
            custom_env = os.environ.copy()
            custom_env["PYTHONIOENCODING"] = "utf-8"

            process = subprocess.Popen(
                cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
                env=custom_env, creationflags=_creationflags()
            )
            
            active_downloads[job_id]["process"] = process
            
            progress_regex = re.compile(r'\[download\]\s+(\d+\.\d+)%')
            speed_regex = re.compile(r'at\s+([0-9.]+[a-zA-Z]+/s)')
            
            for line_bytes in iter(process.stdout.readline, b''):
                if not line_bytes: break
                try:
                    clean_line = line_bytes.decode('utf-8').strip()
                except UnicodeDecodeError:
                    clean_line = line_bytes.decode('mbcs', errors='replace').strip()
                    
                if not clean_line: continue
                
                # חילוץ סטטוסים בזמן אמת לסרטון בודד
                if "[download] Destination:" in clean_line or ("[download]" in clean_line and "has already been downloaded" in clean_line):
                    if active_downloads[job_id]["status"] == "netfree_blocked":
                        active_downloads[job_id]["status"] = "downloading"
                        
                    if ".m4a" in clean_line or "audio" in clean_line.lower():
                        active_downloads[job_id]["phase"] = "audio"
                    else:
                        active_downloads[job_id]["phase"] = "video"
                        
                    if is_custom:
                        try:
                            if "Destination:" in clean_line:
                                dest_path = clean_line.split("Destination:")[1].strip()
                            else:
                                dest_path = clean_line.split("[download]")[1].split("has already")[0].strip()
                                
                            norm_path = dest_path.replace('\\', '/')
                            parts = norm_path.split('/')
                            if len(parts) >= 2:
                                uploader = parts[-2]
                                filename = parts[-1]
                                
                                clean_sub_check = sanitize_filename((subfolder or "").replace('/', ' - '))
                                # לא נדרוס את הערוץ אם זו תיקיית הפלייליסט
                                if uploader not in ["הורדות שונות", "Unknown", clean_sub_check]:
                                    active_downloads[job_id]["channel"] = uploader
                                
                                # מעדכן את שם הסרטון רק אם הוא לא בא מפלייליסט מסודר מראש
                                if not file_prefix:
                                    filename_clean = re.sub(r'\.f\w+', '', filename)
                                    active_downloads[job_id]["title"] = os.path.splitext(filename_clean)[0]
                        except Exception:
                            pass

                elif "[Merger] Merging formats" in clean_line:
                    active_downloads[job_id]["phase"] = "merge"

                match = progress_regex.search(clean_line)
                if match:
                    active_downloads[job_id]["progress"] = float(match.group(1))
                    speed_match = speed_regex.search(clean_line)
                    if speed_match:
                        active_downloads[job_id]["speed"] = speed_match.group(1)
                else:
                    if "Blocked by NetFree" in clean_line or "HTTP Error 418" in clean_line:
                        active_downloads[job_id]["status"] = "netfree_blocked"
                        print(f"  [חסימה - {display_id}] חסום בנטפרי (418)", flush=True)
                    else:
                        print(f"  [yt-dlp: {display_id}] {clean_line[:150]}", flush=True)

            process.stdout.close()
            process.wait()
            
            if process.returncode == 0 or active_downloads[job_id]["progress"] > 0:
                print(f"  [הורדה - {display_id}] הסתיימה בהצלחה!", flush=True)
                active_downloads[job_id]["status"] = "done"
                active_downloads[job_id]["progress"] = 100.0
                active_downloads[job_id]["speed"] = ""
            else:
                if active_downloads[job_id]["status"] == "netfree_blocked":
                    print(f"  [הורדה - {display_id}] בוטלה עקב חסימת נטפרי.", flush=True)
                else:
                    print(f"  [הורדה - {display_id}] נכשלה עם קוד שגיאה {process.returncode}", flush=True)
                    if active_downloads[job_id]["status"] != "canceled":
                        active_downloads[job_id]["status"] = "error"

    except Exception as e:
        print(f"  [הורדה - {display_id}] שגיאת פייתון פנימית: {e}", flush=True)
        if active_downloads.get(job_id) and active_downloads[job_id]["status"] not in ["canceled", "netfree_blocked"]:
            active_downloads[job_id]["status"] = "error"
            
    finally:
        if job_id in active_downloads:
            active_downloads[job_id]["process"] = None

def get_status():
    safe_status = {}
    for j_id, data in active_downloads.items():
        safe_status[j_id] = {
            "status": data["status"],
            "progress": data["progress"],
            "speed": data.get("speed", ""),
            "phase": data.get("phase", "starting"),
            "video_id": data["video_id"],
            "category": data["category"],
            "channel": data["channel"],
            "is_playlist": data.get("is_playlist", False)
        }
        if "title" in data:
            safe_status[j_id]["title"] = data["title"]
            
    return safe_status

def cancel_download(job_id):
    if job_id in active_downloads:
        process = active_downloads[job_id].get("process")
        if process:
            try:
                process.kill()
            except:
                pass
        active_downloads[job_id]["status"] = "canceled"
        print(f"  [הורדה] משימה {job_id[:8]} בוטלה על ידי המשתמש.", flush=True)
        return True
    return False

def clear_completed(job_id):
    if job_id in active_downloads:
        del active_downloads[job_id]
        return True
    return False
