import os
import sys
import subprocess
from pathlib import Path

# 自動檢查並安裝 yt-dlp
try:
    import yt_dlp
except ImportError:
    print("正在安裝 yt-dlp 套件...")
    subprocess.check_call([sys.executable, "-m", "pip", "install", "--upgrade", "yt-dlp"])
    import yt_dlp


def get_safe_download_dir():
    """
    取得具備寫入權限的安全下載路徑：
    1. 優先嘗試在腳本同層建立 downloads 資料夾
    2. 若遭遇 WinError 5 權限不足，自動切換至 Windows 系統使用者「下載」資料夾
    """
    # 取得當前腳本本體所在的絕對路徑目錄
    try:
        script_dir = Path(__file__).resolve().parent
    except NameError:
        script_dir = Path.cwd()

    target_dir = script_dir / "downloads"

    try:
        target_dir.mkdir(parents=True, exist_ok=True)
        # 測試寫入權限
        test_file = target_dir / ".perm_test"
        test_file.touch()
        test_file.unlink()
        return str(target_dir)
    except (PermissionError, OSError):
        # 降級方案：改用 Windows 使用者個人「下載」資料夾
        fallback_dir = Path.home() / "Downloads" / "YT_Downloads"
        fallback_dir.mkdir(parents=True, exist_ok=True)
        print(f"\n[提示] 腳本所在目錄無寫入權限，已自動改用系統下載路徑：\n       {fallback_dir}")
        return str(fallback_dir)


SUPPORTED_VIDEO_CONTAINERS = {
    "1": ("mp4", "MP4 格式 (相容性最高，支援絕大多數裝置與剪輯軟體)"),
    "2": ("mkv", "MKV 格式 (支援多音軌與多字幕封裝，容錯率高)"),
    "3": ("webm", "WebM 格式 (VP9/AV1 + Opus 開源架構，適合網頁播放)"),
    "4": ("mov", "MOV 格式 (QuickTime 封裝，適合 macOS 與 Final Cut Pro)"),
    "5": ("avi", "AVI 格式 (傳統多媒體格式)"),
    "6": ("flv", "FLV 格式 (Flash 串流視訊相容封裝)"),
}

SUPPORTED_AUDIO_CODECS = {
    "1": ("mp3", "MP3 (通用度最高，預設 320kbps 高音質)"),
    "2": ("m4a", "M4A (AAC 編碼，Apple 裝置最佳相容格式)"),
    "3": ("aac", "AAC 原生音訊串流"),
    "4": ("flac", "FLAC (無損壓縮音訊，音質完全不失真)"),
    "5": ("wav", "WAV (無壓縮 PCM 原始音訊，檔案較大)"),
    "6": ("opus", "Opus (低延遲高壓縮比，YouTube 原生最佳音訊編碼)"),
    "7": ("alac", "ALAC (Apple 無損音訊編碼)"),
}


def format_bytes(size):
    if not size:
        return "未知大小"
    for unit in ['B', 'KB', 'MB', 'GB']:
        if size < 1024.0:
            return f"{size:.2f} {unit}"
        size /= 1024.0
    return f"{size:.2f} TB"


def get_video_info(url):
    ydl_opts = {'quiet': True, 'no_warnings': True}
    with yt_dlp.YoutubeDL(ydl_opts) as ydl:
        return ydl.extract_info(url, download=False)


def handle_video_download(url, info, download_dir):
    print("\n" + "=" * 50)
    print("【可選的影片輸出格式】")
    for key, (fmt, desc) in SUPPORTED_VIDEO_CONTAINERS.items():
        print(f" [{key}] {fmt.upper()} - {desc}")
    print("=" * 50)

    choice = input("請選擇目標格式編號 (預設 1 - MP4): ").strip()
    target_format = SUPPORTED_VIDEO_CONTAINERS.get(choice, ("mp4", ""))[0]

    ydl_opts = {
        'format': 'bestvideo+bestaudio/best',
        'merge_output_format': target_format,
        'outtmpl': os.path.join(download_dir, '%(title)s.%(ext)s'),
        'noplaylist': True,
    }

    if target_format in ['avi', 'flv', 'mov']:
        ydl_opts['postprocessors'] = [{
            'key': 'FFmpegVideoConvertor',
            'preferedformat': target_format,
        }]

    print(f"\n[執行下載] 畫質：最高畫質 + 最佳音軌 | 目標格式：{target_format.upper()}")
    with yt_dlp.YoutubeDL(ydl_opts) as ydl:
        ydl.download([url])


def handle_audio_download(url, info, download_dir):
    print("\n" + "=" * 50)
    print("【可選的音訊轉換格式】")
    for key, (codec, desc) in SUPPORTED_AUDIO_CODECS.items():
        print(f" [{key}] {codec.upper()} - {desc}")
    print("=" * 50)

    choice = input("請選擇目標音訊格式編號 (預設 1 - MP3): ").strip()
    target_codec = SUPPORTED_AUDIO_CODECS.get(choice, ("mp3", ""))[0]

    ydl_opts = {
        'format': 'bestaudio/best',
        'outtmpl': os.path.join(download_dir, '%(title)s.%(ext)s'),
        'noplaylist': True,
        'postprocessors': [{
            'key': 'FFmpegExtractAudio',
            'preferredcodec': target_codec,
            'preferredquality': '320' if target_codec == 'mp3' else None,
        }],
    }

    print(f"\n[執行下載] 擷取最佳音軌並轉碼為：{target_codec.upper()}")
    with yt_dlp.YoutubeDL(ydl_opts) as ydl:
        ydl.download([url])


def handle_raw_stream_selection(url, info, download_dir):
    formats = info.get('formats', [])
    if not formats:
        print("未偵測到串流軌道資訊。")
        return

    print("\n" + "=" * 90)
    print(f"{'Format ID':<12} {'副檔名':<8} {'類型':<10} {'解析度/頻寬':<18} {'FPS':<6} {'編碼格式':<18} {'預估大小'}")
    print("-" * 90)

    for f in formats:
        fid = f.get('format_id', 'N/A')
        ext = f.get('ext', 'N/A')
        vcodec = f.get('vcodec', 'none')
        acodec = f.get('acodec', 'none')
        fps = str(f.get('fps', '')) if f.get('fps') else '-'
        filesize = format_bytes(f.get('filesize') or f.get('filesize_approx'))

        if vcodec != 'none' and acodec != 'none':
            stream_type = "影音合成"
            detail = f"{f.get('width', '?')}x{f.get('height', '?')}"
            codec_str = f"{vcodec[:7]}+{acodec[:7]}"
        elif vcodec != 'none':
            stream_type = "純影像軌"
            detail = f"{f.get('width', '?')}x{f.get('height', '?')} ({f.get('format_note', '')})"
            codec_str = vcodec
        elif acodec != 'none':
            stream_type = "純音訊軌"
            abr = f.get('abr', '?')
            detail = f"{abr} kbps"
            codec_str = acodec
        else:
            stream_type = "其他"
            detail = f.get('format_note', '-')
            codec_str = "unknown"

        print(f"{fid:<12} {ext:<8} {stream_type:<10} {detail:<18} {fps:<6} {codec_str:<18} {filesize}")

    print("=" * 90)
    print("【輸入範例】")
    print(" - 單軌下載：直接輸入編號，如 `137`")
    print(" - 合併軌道：輸入 `影像編號+音訊編號`，如 `137+140`（將自動以 FFmpeg 合成）")
    format_selection = input("\n請輸入想要下載的 Format ID 組合: ").strip()

    if not format_selection:
        print("未輸入編號，取消操作。")
        return

    ydl_opts = {
        'format': format_selection,
        'merge_output_format': 'mp4',
        'outtmpl': os.path.join(download_dir, '%(title)s.%(ext)s'),
    }

    print(f"\n[執行下載] 正在抓取指定串流：{format_selection}")
    with yt_dlp.YoutubeDL(ydl_opts) as ydl:
        ydl.download([url])


def main():
    print("=" * 60)
    print("       yt-dlp 全格式自選進階下載器（權限加強版）")
    print("=" * 60)

    # 1. 取得絕對安全且有權限的儲存目錄
    download_dir = get_safe_download_dir()
    print(f"檔案儲存路徑已設定為：{download_dir}\n")

    url = input("請輸入 YouTube 影片網址: ").strip()
    if not url:
        print("未輸入網址，程式結束。")
        return

    print("\n正在向 YouTube 解析影片資訊與可用串流軌道，請稍候...")
    try:
        info = get_video_info(url)
    except Exception as e:
        print(f"\n[解析失敗]：{e}")
        return

    title = info.get('title', '未知標題')
    duration = info.get('duration', 0)
    uploader = info.get('uploader', '未知上傳者')
    print(f"\n影片標題：{title}")
    print(f"發布頻道：{uploader}")
    print(f"影片長度：{duration // 60} 分 {duration % 60} 秒")

    print("\n請選擇下載處理模式：")
    print(" [1] 影片下載（自動選最佳畫質音訊，並由您挑選輸出格式：MP4 / MKV / WebM / MOV / AVI 等）")
    print(" [2] 純音訊下載（自動提取最高音質，並轉碼為：MP3 / M4A / FLAC / WAV / OPUS 等）")
    print(" [3] 進階串流手動模式（列出伺服器上所有 Format ID 軌道供您自訂搭配）")

    mode = input("\n請選擇模式 (1-3，預設 1): ").strip()

    try:
        if mode == '2':
            handle_audio_download(url, info, download_dir)
        elif mode == '3':
            handle_raw_stream_selection(url, info, download_dir)
        else:
            handle_video_download(url, info, download_dir)
        print(f"\n[成功] 處理完成！檔案儲存於：{download_dir}")
    except Exception as e:
        print(f"\n[下載處理失敗]：{e}")


if __name__ == "__main__":
    if len(sys.argv) > 1 and sys.argv[1] == "--cli":
        try:
            main()
        except Exception as general_err:
            print(f"\n程式異常終止：{general_err}")
        finally:
            input("\n請按 Enter 鍵退出程式...")
    else:
        try:
            import YT_GUI
            YT_GUI.main()
        except Exception as e:
            print(f"[提示] 無法啟動圖形介面 ({e})，切換至命令列模式：\n")
            try:
                main()
            except Exception as general_err:
                print(f"\n程式異常終止：{general_err}")
            finally:
                input("\n請按 Enter 鍵退出程式...")