"""
Modern Windows 10/11 Fluent Desktop GUI for VideoDetection.
Hỗ trợ quy trình tự động đồng bộ (Auto-Sync Data & Target) và đa kịch bản so sánh:
- 1 Video Target vs Toàn bộ kho Data/
- 1 Video Target vs 1 vài video tham chiếu được tích chọn
Số liệu đặc trưng (V-JEPA 2 + NeuralFP) được nạp sẵn tức thì khi mở app.
"""
from __future__ import annotations

import json
import logging
import os
import subprocess
import sys
import threading
import time
from pathlib import Path
from tkinter import filedialog, messagebox, ttk
import tkinter as tk

from ..alignment.matcher import MatchSegment
from ..core.config import CONFIG
from ..core.paths import APP_ROOT, DATA_DIR, TARGET_DIR, TEMP_DIR, clean_temp_dir, get_ffmpeg_path
from ..indexing.feature_store import CachedVideoFeatures, SUPPORTED_EXTENSIONS
from ..ingestion.youtube_downloader import DEFAULT_SECTION_1H
from ..pipeline import VideoDetectionPipeline

logger = logging.getLogger(__name__)


class VideoDetectionGUI:
    def __init__(self, root: tk.Tk) -> None:
        self.root = root
        self.root.title("VideoDetection Studio - Nhận Diện Trùng Lặp Video Bản Quyền (V-JEPA 2 & NeuralFP)")
        self.root.geometry("1180x760")
        self.root.minsize(1020, 640)

        # Bảng màu Fluent Dark Mode chuẩn Windows 11
        self.colors = {
            "bg_base": "#202020",
            "bg_card": "#2b2b2b",
            "bg_card_hover": "#353535",
            "bg_input": "#1a1a1a",
            "accent": "#0078d4",
            "accent_hover": "#106ebe",
            "accent_pressed": "#005a9e",
            "text_primary": "#ffffff",
            "text_secondary": "#cccccc",
            "text_muted": "#888888",
            "border": "#3d3d3d",
            "danger": "#e81123",
            "success": "#107c41",
            "warning": "#d83b01",
            "highlight": "#284b63",
        }

        self.root.configure(bg=self.colors["bg_base"])

        self.pipeline: VideoDetectionPipeline | None = None
        self.is_processing = False

        # Dữ liệu cache trong bộ nhớ
        self.cached_targets: List[CachedVideoFeatures] = []
        self.cached_references: List[CachedVideoFeatures] = []
        self.selected_target_video: CachedVideoFeatures | None = None

        # Checkbox mapping cho video tham chiếu: video_id -> tk.BooleanVar
        self.ref_check_vars: dict[str, tk.BooleanVar] = {}

        self.last_comparison_results: List[MatchSegment] = []

        self._setup_styles()
        self._build_ui()
        self._initialize_pipeline_and_sync()

    def _setup_styles(self) -> None:
        style = ttk.Style()
        style.theme_use("clam")

        # Tabs
        style.configure(
            "TNotebook",
            background=self.colors["bg_base"],
            borderwidth=0,
        )
        style.configure(
            "TNotebook.Tab",
            background=self.colors["bg_card"],
            foreground=self.colors["text_secondary"],
            padding=[18, 9],
            font=("Segoe UI", 10, "bold"),
            borderwidth=0,
        )
        style.map(
            "TNotebook.Tab",
            background=[("selected", self.colors["accent"]), ("active", self.colors["bg_card_hover"])],
            foreground=[("selected", "#ffffff"), ("active", "#ffffff")],
        )

        # Treeview (Bảng kết quả và danh sách)
        style.configure(
            "Treeview",
            background=self.colors["bg_card"],
            foreground=self.colors["text_primary"],
            fieldbackground=self.colors["bg_card"],
            font=("Segoe UI", 9),
            rowheight=26,
            borderwidth=0,
        )
        style.configure(
            "Treeview.Heading",
            background="#181818",
            foreground=self.colors["text_primary"],
            font=("Segoe UI", 9, "bold"),
            relief="flat",
            padding=[6, 5],
        )
        style.map(
            "Treeview",
            background=[("selected", self.colors["accent"])],
            foreground=[("selected", "#ffffff")],
        )

        # Progressbar
        style.configure(
            "Horizontal.TProgressbar",
            background=self.colors["accent"],
            troughcolor=self.colors["bg_card"],
            borderwidth=0,
            thickness=6,
        )

    def _build_ui(self) -> None:
        # Header Top Bar
        header = tk.Frame(self.root, bg=self.colors["bg_base"], height=60)
        header.pack(fill="x", padx=18, pady=(12, 8))

        lbl_title = tk.Label(
            header,
            text="VideoDetection Studio",
            font=("Segoe UI", 16, "bold"),
            bg=self.colors["bg_base"],
            fg=self.colors["text_primary"],
        )
        lbl_title.pack(side="left")

        lbl_sub = tk.Label(
            header,
            text=" |  Đối Soát Bản Quyền Video Đa Phương Thức (V-JEPA 2 & NeuralFP)",
            font=("Segoe UI", 9),
            bg=self.colors["bg_base"],
            fg=self.colors["text_secondary"],
        )
        lbl_sub.pack(side="left", pady=(4, 0))

        # Status badge & Sync button in header
        self.btn_sync_now = tk.Button(
            header,
            text="🔄 Quét & Đồng Bộ Video Mới",
            font=("Segoe UI", 9, "bold"),
            bg=self.colors["bg_card_hover"],
            fg=self.colors["text_primary"],
            activebackground=self.colors["accent"],
            activeforeground="#ffffff",
            bd=0,
            padx=12,
            pady=5,
            command=self.on_force_sync,
            cursor="hand2",
        )
        self.btn_sync_now.pack(side="right", padx=(10, 0))

        self.badge_status = tk.Label(
            header,
            text="Đang nạp mô hình & dữ liệu...",
            font=("Segoe UI", 9, "bold"),
            bg="#3a3a3a",
            fg="#ffffaa",
            padx=12,
            pady=4,
        )
        self.badge_status.pack(side="right")

        # Main Notebook
        self.notebook = ttk.Notebook(self.root)
        self.notebook.pack(fill="both", expand=True, padx=18, pady=(0, 8))

        self.tab_compare = tk.Frame(self.notebook, bg=self.colors["bg_base"])
        self.tab_download = tk.Frame(self.notebook, bg=self.colors["bg_base"])
        self.tab_manage = tk.Frame(self.notebook, bg=self.colors["bg_base"])
        self.tab_settings = tk.Frame(self.notebook, bg=self.colors["bg_base"])

        self.notebook.add(self.tab_compare, text="  🎯 Trung Tâm Đối Soát & So Sánh  ")
        self.notebook.add(self.tab_download, text="  📥 Tải Video YouTube  ")
        self.notebook.add(self.tab_manage, text="  📁 Quản Lý File Data/ & Target/  ")
        self.notebook.add(self.tab_settings, text="  ⚙️ Cấu Hình & Tiện Ích  ")

        self._build_compare_tab()
        self._build_download_tab()
        self._build_manage_tab()
        self._build_settings_tab()

        # Bottom Status Bar & Progress
        bottom_frame = tk.Frame(self.root, bg=self.colors["bg_card"], height=40)
        bottom_frame.pack(fill="x", side="bottom")

        self.progress_bar = ttk.Progressbar(
            bottom_frame,
            style="Horizontal.TProgressbar",
            mode="determinate",
        )
        self.progress_bar.pack(fill="x", side="top")

        status_inner = tk.Frame(bottom_frame, bg=self.colors["bg_card"])
        status_inner.pack(fill="x", padx=15, pady=5)

        self.status_lbl = tk.Label(
            status_inner,
            text="Đang chuẩn bị hệ thống...",
            font=("Segoe UI", 9),
            bg=self.colors["bg_card"],
            fg=self.colors["text_secondary"],
        )
        self.status_lbl.pack(side="left")

        self.stats_summary_lbl = tk.Label(
            status_inner,
            text="Kho Data: 0 video | Target: 0 video",
            font=("Segoe UI", 9),
            bg=self.colors["bg_card"],
            fg=self.colors["text_muted"],
        )
        self.stats_summary_lbl.pack(side="right")

    # ==================== TAB 1: COMPARE CENTER ====================
    def _build_compare_tab(self) -> None:
        # Container chia làm 2 tầng: Tầng trên chọn đối tượng, Tầng dưới xem kết quả
        paned_vertical = tk.PanedWindow(self.tab_compare, orient="vertical", bg=self.colors["bg_base"], bd=0, sashwidth=4)
        paned_vertical.pack(fill="both", expand=True)

        top_selection_frame = tk.Frame(paned_vertical, bg=self.colors["bg_base"], height=320)
        bottom_results_frame = tk.Frame(paned_vertical, bg=self.colors["bg_base"])

        paned_vertical.add(top_selection_frame)
        paned_vertical.add(bottom_results_frame)

        # Top Selection chia làm 2 cột: Cột trái (Target Video), Cột phải (Reference Data Selection)
        top_selection_frame.columnconfigure(0, weight=4)
        top_selection_frame.columnconfigure(1, weight=6)
        top_selection_frame.rowconfigure(0, weight=1)

        # Cột Trái: Chọn Video Target
        col_target = tk.Frame(top_selection_frame, bg=self.colors["bg_card"], padx=12, pady=10)
        col_target.grid(row=0, column=0, sticky="nsew", padx=(0, 6), pady=6)

        header_target = tk.Frame(col_target, bg=self.colors["bg_card"])
        header_target.pack(fill="x", pady=(0, 6))

        tk.Label(
            header_target,
            text="1. Chọn Video Mục Tiêu (Target)",
            font=("Segoe UI", 10, "bold"),
            bg=self.colors["bg_card"],
            fg=self.colors["text_primary"],
        ).pack(side="left")

        self.lbl_target_count = tk.Label(
            header_target,
            text="(0 video)",
            font=("Segoe UI", 9),
            bg=self.colors["bg_card"],
            fg=self.colors["text_muted"],
        )
        self.lbl_target_count.pack(side="left", padx=(6, 0))

        # Danh sách Treeview Target
        target_tree_box = tk.Frame(col_target, bg=self.colors["bg_card"])
        target_tree_box.pack(fill="both", expand=True)

        self.tree_targets = ttk.Treeview(
            target_tree_box,
            columns=("name", "duration", "status"),
            show="headings",
            selectmode="browse",
        )
        self.tree_targets.heading("name", text="Tên File Target")
        self.tree_targets.heading("duration", text="Thời Lượng")
        self.tree_targets.heading("status", text="Trạng Thái")

        self.tree_targets.column("name", width=220, anchor="w")
        self.tree_targets.column("duration", width=75, anchor="center")
        self.tree_targets.column("status", width=95, anchor="center")

        target_scroll = ttk.Scrollbar(target_tree_box, orient="vertical", command=self.tree_targets.yview)
        self.tree_targets.configure(yscrollcommand=target_scroll.set)
        self.tree_targets.pack(side="left", fill="both", expand=True)
        target_scroll.pack(side="right", fill="y")

        self.tree_targets.bind("<<TreeviewSelect>>", self._on_target_selected)

        # Cột Phải: Chọn Video Tham Chiếu (Reference)
        col_ref = tk.Frame(top_selection_frame, bg=self.colors["bg_card"], padx=12, pady=10)
        col_ref.grid(row=0, column=1, sticky="nsew", padx=(6, 0), pady=6)

        header_ref = tk.Frame(col_ref, bg=self.colors["bg_card"])
        header_ref.pack(fill="x", pady=(0, 6))

        tk.Label(
            header_ref,
            text="2. Chọn Đối Tượng Tham Chiếu (Kho Data)",
            font=("Segoe UI", 10, "bold"),
            bg=self.colors["bg_card"],
            fg=self.colors["text_primary"],
        ).pack(side="left")

        # Radio options so sánh
        self.compare_mode_var = tk.StringVar(value="all")

        radio_box = tk.Frame(col_ref, bg=self.colors["bg_card"])
        radio_box.pack(fill="x", pady=(0, 6))

        self.radio_all = tk.Radiobutton(
            radio_box,
            text="So sánh với TOÀN BỘ kho Data/ (Khuyên dùng)",
            variable=self.compare_mode_var,
            value="all",
            font=("Segoe UI", 9, "bold"),
            bg=self.colors["bg_card"],
            fg=self.colors["text_primary"],
            selectcolor=self.colors["bg_input"],
            activebackground=self.colors["bg_card"],
            activeforeground="#ffffff",
            command=self._on_compare_mode_changed,
        )
        self.radio_all.pack(side="left")

        self.radio_selected = tk.Radiobutton(
            radio_box,
            text="Chỉ so sánh với các video được chọn bên dưới:",
            variable=self.compare_mode_var,
            value="selected",
            font=("Segoe UI", 9),
            bg=self.colors["bg_card"],
            fg=self.colors["text_secondary"],
            selectcolor=self.colors["bg_input"],
            activebackground=self.colors["bg_card"],
            activeforeground="#ffffff",
            command=self._on_compare_mode_changed,
        )
        self.radio_selected.pack(side="left", padx=(15, 0))

        # Checklist Controls
        self.ref_list_controls = tk.Frame(col_ref, bg=self.colors["bg_card"])
        self.ref_list_controls.pack(fill="x", pady=(0, 4))

        tk.Button(
            self.ref_list_controls,
            text="✓ Chọn Tất Cả",
            font=("Segoe UI", 8),
            bg=self.colors["bg_card_hover"],
            fg=self.colors["text_secondary"],
            bd=0,
            padx=8,
            pady=3,
            command=self._select_all_refs,
            cursor="hand2",
        ).pack(side="left", padx=(0, 5))

        tk.Button(
            self.ref_list_controls,
            text="✗ Bỏ Chọn Hết",
            font=("Segoe UI", 8),
            bg=self.colors["bg_card_hover"],
            fg=self.colors["text_secondary"],
            bd=0,
            padx=8,
            pady=3,
            command=self._deselect_all_refs,
            cursor="hand2",
        ).pack(side="left")

        # Scrollable Canvas cho checklist video tham chiếu
        ref_canvas_box = tk.Frame(col_ref, bg=self.colors["bg_input"], bd=1, relief="solid")
        ref_canvas_box.pack(fill="both", expand=True, pady=(2, 8))

        self.ref_canvas = tk.Canvas(ref_canvas_box, bg=self.colors["bg_input"], highlightthickness=0)
        ref_scroll = ttk.Scrollbar(ref_canvas_box, orient="vertical", command=self.ref_canvas.yview)
        self.ref_checklist_inner = tk.Frame(self.ref_canvas, bg=self.colors["bg_input"])

        self.ref_checklist_inner.bind(
            "<Configure>",
            lambda e: self.ref_canvas.configure(scrollregion=self.ref_canvas.bbox("all")),
        )
        self.ref_canvas.create_window((0, 0), window=self.ref_checklist_inner, anchor="nw")
        self.ref_canvas.configure(yscrollcommand=ref_scroll.set)

        self.ref_canvas.pack(side="left", fill="both", expand=True)
        ref_scroll.pack(side="right", fill="y")

        # Action Bar so sánh
        action_bar = tk.Frame(col_ref, bg=self.colors["bg_card"])
        action_bar.pack(fill="x")

        self.btn_run_compare = tk.Button(
            action_bar,
            text="⚡ BẮT ĐẦU ĐỐI SOÁT (TỨC THÌ < 0.1s)",
            font=("Segoe UI", 10, "bold"),
            bg=self.colors["accent"],
            fg="#ffffff",
            activebackground=self.colors["accent_hover"],
            activeforeground="#ffffff",
            bd=0,
            padx=16,
            pady=8,
            command=self.on_run_instant_comparison,
            cursor="hand2",
        )
        self.btn_run_compare.pack(side="right")

        # ==================== TẦNG DƯỚI: BẢNG KẾT QUẢ ====================
        bottom_card = tk.Frame(bottom_results_frame, bg=self.colors["bg_card"], padx=12, pady=10)
        bottom_card.pack(fill="both", expand=True, pady=(6, 0))

        header_res = tk.Frame(bottom_card, bg=self.colors["bg_card"])
        header_res.pack(fill="x", pady=(0, 6))

        tk.Label(
            header_res,
            text="3. Bảng Kết Quả Đối Soát Trùng Lặp",
            font=("Segoe UI", 10, "bold"),
            bg=self.colors["bg_card"],
            fg=self.colors["text_primary"],
        ).pack(side="left")

        self.lbl_match_summary = tk.Label(
            header_res,
            text="(Chưa thực hiện so sánh)",
            font=("Segoe UI", 9),
            bg=self.colors["bg_card"],
            fg=self.colors["text_muted"],
        )
        self.lbl_match_summary.pack(side="left", padx=(10, 0))

        self.btn_export_res = tk.Button(
            header_res,
            text="💾 Xuất Báo Cáo JSON",
            font=("Segoe UI", 9),
            bg=self.colors["bg_card_hover"],
            fg=self.colors["text_secondary"],
            bd=0,
            padx=10,
            pady=4,
            command=self.on_export_comparison_results,
            cursor="hand2",
        )
        self.btn_export_res.pack(side="right")

        # Treeview Results
        table_container = tk.Frame(bottom_card, bg=self.colors["bg_card"])
        table_container.pack(fill="both", expand=True)

        cols = (
            "target_video",
            "ref_video",
            "target_range",
            "ref_range",
            "duration",
            "confidence",
            "match_type",
            "status",
        )
        self.tree_matches = ttk.Treeview(table_container, columns=cols, show="headings", selectmode="browse")

        self.tree_matches.heading("target_video", text="Video Mục Tiêu")
        self.tree_matches.heading("ref_video", text="Video Gốc Trùng Khớp")
        self.tree_matches.heading("target_range", text="Khoảng Target")
        self.tree_matches.heading("ref_range", text="Khoảng Gốc")
        self.tree_matches.heading("duration", text="Thời Lượng")
        self.tree_matches.heading("confidence", text="Độ Trùng")
        self.tree_matches.heading("match_type", text="Dạng Nhận Diện")
        self.tree_matches.heading("status", text="Cảnh Báo")

        self.tree_matches.column("target_video", width=220, anchor="w")
        self.tree_matches.column("ref_video", width=200, anchor="w")
        self.tree_matches.column("target_range", width=120, anchor="center")
        self.tree_matches.column("ref_range", width=120, anchor="center")
        self.tree_matches.column("duration", width=85, anchor="center")
        self.tree_matches.column("confidence", width=95, anchor="center")
        self.tree_matches.column("match_type", width=130, anchor="center")
        self.tree_matches.column("status", width=100, anchor="center")

        res_scroll = ttk.Scrollbar(table_container, orient="vertical", command=self.tree_matches.yview)
        self.tree_matches.configure(yscrollcommand=res_scroll.set)
        self.tree_matches.pack(side="left", fill="both", expand=True)
        res_scroll.pack(side="right", fill="y")

    # ==================== TAB: YOUTUBE DOWNLOADER ====================
    def _build_download_tab(self) -> None:
        outer = tk.Frame(self.tab_download, bg=self.colors["bg_base"], padx=18, pady=12)
        outer.pack(fill="both", expand=True)

        card = tk.Frame(outer, bg=self.colors["bg_card"], padx=20, pady=16)
        card.pack(fill="both", expand=True)

        # Header Title
        tk.Label(
            card,
            text="📥 Tải Video Hoặc Phân Đoạn Chỉ Định Từ YouTube",
            font=("Segoe UI", 12, "bold"),
            bg=self.colors["bg_card"],
            fg=self.colors["text_primary"],
        ).pack(anchor="w")

        tk.Label(
            card,
            text="Hỗ trợ Video ID (11 ký tự), Link đầy đủ, Link rút gọn youtu.be, Shorts, Embed, Live.\nChỉ tải đúng phân đoạn dữ liệu cần thiết (Range Request), không tốn băng thông và thời gian tải cả file dài.",
            font=("Segoe UI", 9),
            justify="left",
            bg=self.colors["bg_card"],
            fg=self.colors["text_secondary"],
        ).pack(anchor="w", pady=(3, 14))

        # 1. URL Input Box
        tk.Label(
            card,
            text="1. Nhập Link Video YouTube hoặc Video ID (11 ký tự):",
            font=("Segoe UI", 10, "bold"),
            bg=self.colors["bg_card"],
            fg=self.colors["text_primary"],
        ).pack(anchor="w")

        url_input_box = tk.Frame(card, bg=self.colors["bg_card"])
        url_input_box.pack(fill="x", pady=(5, 12))

        self.entry_yt_url = tk.Entry(
            url_input_box,
            font=("Segoe UI", 10),
            bg=self.colors["bg_input"],
            fg=self.colors["text_primary"],
            insertbackground="#ffffff",
            bd=1,
            relief="solid",
        )
        self.entry_yt_url.pack(side="left", fill="x", expand=True, ipady=5, padx=(0, 8))

        btn_paste = tk.Button(
            url_input_box,
            text="📋 Dán Clipboard",
            font=("Segoe UI", 9, "bold"),
            bg=self.colors["bg_card_hover"],
            fg=self.colors["text_primary"],
            activebackground=self.colors["accent"],
            activeforeground="#ffffff",
            bd=0,
            padx=12,
            pady=5,
            command=self._on_paste_yt_clipboard,
            cursor="hand2",
        )
        btn_paste.pack(side="right")

        # 2. Section Input Box
        tk.Label(
            card,
            text="2. Phân đoạn thời gian cần tải (Mặc định: 1 Giờ đầu tiên):",
            font=("Segoe UI", 10, "bold"),
            bg=self.colors["bg_card"],
            fg=self.colors["text_primary"],
        ).pack(anchor="w")

        section_box = tk.Frame(card, bg=self.colors["bg_card"])
        section_box.pack(fill="x", pady=(5, 4))

        self.entry_yt_section = tk.Entry(
            section_box,
            font=("Segoe UI", 10),
            bg=self.colors["bg_input"],
            fg=self.colors["text_primary"],
            insertbackground="#ffffff",
            bd=1,
            relief="solid",
        )
        self.entry_yt_section.pack(side="left", fill="x", expand=True, ipady=5, padx=(0, 8))
        self.entry_yt_section.insert(0, "00:00:00 - 01:00:00")

        # Preset Buttons
        preset_box = tk.Frame(card, bg=self.colors["bg_card"])
        preset_box.pack(fill="x", pady=(0, 12))

        def set_sec(val: str):
            self.entry_yt_section.delete(0, tk.END)
            self.entry_yt_section.insert(0, val)

        tk.Button(
            preset_box,
            text="⏱️ 1 Giờ Đầu (Mặc định)",
            font=("Segoe UI", 8),
            bg=self.colors["bg_card_hover"],
            fg=self.colors["text_secondary"],
            bd=0,
            padx=8,
            pady=3,
            command=lambda: set_sec("00:00:00 - 01:00:00"),
            cursor="hand2",
        ).pack(side="left", padx=(0, 6))

        tk.Button(
            preset_box,
            text="⏱️ 30 Phút Đầu",
            font=("Segoe UI", 8),
            bg=self.colors["bg_card_hover"],
            fg=self.colors["text_secondary"],
            bd=0,
            padx=8,
            pady=3,
            command=lambda: set_sec("00:00:00 - 00:30:00"),
            cursor="hand2",
        ).pack(side="left", padx=(0, 6))

        tk.Button(
            preset_box,
            text="⏱️ 15 Phút Đầu",
            font=("Segoe UI", 8),
            bg=self.colors["bg_card_hover"],
            fg=self.colors["text_secondary"],
            bd=0,
            padx=8,
            pady=3,
            command=lambda: set_sec("00:00:00 - 00:15:00"),
            cursor="hand2",
        ).pack(side="left", padx=(0, 6))

        tk.Button(
            preset_box,
            text="🎬 Toàn Bộ Video",
            font=("Segoe UI", 8),
            bg=self.colors["bg_card_hover"],
            fg=self.colors["text_secondary"],
            bd=0,
            padx=8,
            pady=3,
            command=lambda: set_sec("all"),
            cursor="hand2",
        ).pack(side="left")

        # 3. Destination Directory
        tk.Label(
            card,
            text="3. Lưu video vào thư mục:",
            font=("Segoe UI", 10, "bold"),
            bg=self.colors["bg_card"],
            fg=self.colors["text_primary"],
        ).pack(anchor="w")

        self.yt_dest_var = tk.StringVar(value="target")

        dest_box = tk.Frame(card, bg=self.colors["bg_card"])
        dest_box.pack(fill="x", pady=(4, 12))

        tk.Radiobutton(
            dest_box,
            text="🎯 Thư mục Target/ (Video cần kiểm tra đối soát - Mặc định)",
            variable=self.yt_dest_var,
            value="target",
            font=("Segoe UI", 9, "bold"),
            bg=self.colors["bg_card"],
            fg=self.colors["text_primary"],
            selectcolor=self.colors["bg_input"],
            activebackground=self.colors["bg_card"],
            activeforeground="#ffffff",
        ).pack(side="left")

        tk.Radiobutton(
            dest_box,
            text="📁 Thư mục Data/ (Kho video gốc bản quyền của chủ sở hữu)",
            variable=self.yt_dest_var,
            value="data",
            font=("Segoe UI", 9),
            bg=self.colors["bg_card"],
            fg=self.colors["text_secondary"],
            selectcolor=self.colors["bg_input"],
            activebackground=self.colors["bg_card"],
            activeforeground="#ffffff",
        ).pack(side="left", padx=(20, 0))

        # 4. Auto sync checkbox
        self.yt_auto_sync_var = tk.BooleanVar(value=True)
        tk.Checkbutton(
            card,
            text="✅ Tự động trích xuất đặc trưng AI (V-JEPA 2 & NeuralFP) ngay sau khi tải xong",
            variable=self.yt_auto_sync_var,
            font=("Segoe UI", 9),
            bg=self.colors["bg_card"],
            fg=self.colors["text_primary"],
            selectcolor=self.colors["bg_input"],
            activebackground=self.colors["bg_card"],
            activeforeground="#ffffff",
        ).pack(anchor="w", pady=(0, 14))

        # 5. Start Download Action Button
        self.btn_yt_download = tk.Button(
            card,
            text="⚡ BẮT ĐẦU TẢI VIDEO YOUTUBE",
            font=("Segoe UI", 10, "bold"),
            bg=self.colors["accent"],
            fg="#ffffff",
            activebackground=self.colors["accent_hover"],
            activeforeground="#ffffff",
            bd=0,
            padx=20,
            pady=8,
            command=self.on_start_yt_download,
            cursor="hand2",
        )
        self.btn_yt_download.pack(anchor="w", pady=(0, 12))

        # 6. Status & Output Log Console
        tk.Label(
            card,
            text="Nhật Ký Tiến Độ Tải & Đồng Bộ:",
            font=("Segoe UI", 9, "bold"),
            bg=self.colors["bg_card"],
            fg=self.colors["text_secondary"],
        ).pack(anchor="w")

        self.txt_yt_log = tk.Text(
            card,
            height=6,
            bg=self.colors["bg_input"],
            fg="#a8d5e5",
            font=("Consolas", 9),
            bd=1,
            relief="solid",
            state="disabled",
            wrap="word",
        )
        self.txt_yt_log.pack(fill="both", expand=True, pady=(4, 0))

    def _on_paste_yt_clipboard(self) -> None:
        try:
            clipboard_text = self.root.clipboard_get().strip()
            if clipboard_text:
                self.entry_yt_url.delete(0, tk.END)
                self.entry_yt_url.insert(0, clipboard_text)
        except Exception:
            pass

    def _append_yt_log(self, text: str) -> None:
        self.txt_yt_log.configure(state="normal")
        self.txt_yt_log.insert(tk.END, text + "\n")
        self.txt_yt_log.see(tk.END)
        self.txt_yt_log.configure(state="disabled")

    def _navigate_to_download(self, dest: str) -> None:
        self.yt_dest_var.set(dest)
        self.notebook.select(self.tab_download)
        self.entry_yt_url.focus_set()

    def on_start_yt_download(self) -> None:
        if self.is_processing or not self.pipeline:
            return

        raw_url = self.entry_yt_url.get().strip()
        if not raw_url:
            messagebox.showwarning("Thiếu thông tin", "Vui lòng nhập Link video YouTube hoặc ID video (11 ký tự).")
            return

        raw_section = self.entry_yt_section.get().strip()
        dest_choice = self.yt_dest_var.get()
        auto_sync = self.yt_auto_sync_var.get()

        self.txt_yt_log.configure(state="normal")
        self.txt_yt_log.delete("1.0", tk.END)
        self.txt_yt_log.configure(state="disabled")

        self._append_yt_log(f"[*] Bắt đầu tải video từ YouTube: {raw_url}")
        self._append_yt_log(f"    Phân đoạn  : {raw_section if raw_section else '1 giờ đầu (mặc định)'}")
        self._append_yt_log(f"    Thư mục lưu: {dest_choice.upper()}/")
        self._append_yt_log(f"    Tự động AI : {'Có' if auto_sync else 'Không'}")

        def task(cb):
            def log_cb(pct: int, msg: str):
                cb(pct, msg)
                self.root.after(0, lambda: self._append_yt_log(f"[{pct}%] {msg}"))

            res = self.pipeline.download_youtube_video(
                url_or_id=raw_url,
                section=raw_section,
                dest=dest_choice,
                auto_sync=auto_sync,
                progress_callback=log_cb,
            )
            return res

        def on_success(res):
            if res.success and res.file_path:
                self._append_yt_log(f"[HOÀN TẤT] Đã tải thành công file: {res.file_path.name}")
                self._append_yt_log(f"            Đường dẫn: {res.file_path}")
                # Cập nhật danh sách hiển thị
                self.cached_references = self.pipeline.get_cached_references()
                self.cached_targets = self.pipeline.get_cached_targets()
                self.refresh_all_views()

                target_tab_text = "Target/" if dest_choice == "target" else "Data/"
                messagebox.showinfo(
                    "Tải video thành công",
                    f"Đã tải thành công video YouTube vào {target_tab_text}!\n\n"
                    f"• Tiêu đề: {res.title}\n"
                    f"• File: {res.file_path.name}\n"
                    f"• Thư mục: {res.dest_dir}\n"
                    f"{'• Đã trích xuất đặc trưng AI sẵn sàng đối soát!' if auto_sync else ''}",
                )
            else:
                self._append_yt_log(f"[THẤT BẠI] Lỗi: {res.error_message}")
                messagebox.showerror("Lỗi tải video", f"Không thể tải video YouTube:\n{res.error_message}")

        self._run_task(
            task_func=task,
            on_success=on_success,
            title=f"Tải video YouTube",
        )

    # ==================== TAB 2: MANAGE DIRECTORIES ====================
    def _build_manage_tab(self) -> None:
        manage_frame = tk.Frame(self.tab_manage, bg=self.colors["bg_base"], padx=15, pady=15)
        manage_frame.pack(fill="both", expand=True)

        # 2 Khối Quản lý Data và Target
        manage_frame.columnconfigure(0, weight=1)
        manage_frame.columnconfigure(1, weight=1)
        manage_frame.rowconfigure(0, weight=1)

        # Card Kho Data/
        card_data = tk.Frame(manage_frame, bg=self.colors["bg_card"], padx=15, pady=15)
        card_data.grid(row=0, column=0, sticky="nsew", padx=(0, 8))

        tk.Label(
            card_data,
            text="📁 Kho Video Gốc Chủ Sở Hữu (Data/)",
            font=("Segoe UI", 11, "bold"),
            bg=self.colors["bg_card"],
            fg=self.colors["text_primary"],
        ).pack(anchor="w")

        tk.Label(
            card_data,
            text="Người dùng chỉ cần copy/paste video gốc vào thư mục Data/.\nPhần mềm sẽ tự động phát hiện, trích xuất V-JEPA 2 & NeuralFP và lưu vĩnh viễn.",
            font=("Segoe UI", 9),
            justify="left",
            bg=self.colors["bg_card"],
            fg=self.colors["text_secondary"],
        ).pack(anchor="w", pady=(5, 12))

        btn_box_data = tk.Frame(card_data, bg=self.colors["bg_card"])
        btn_box_data.pack(fill="x", pady=(0, 10))

        tk.Button(
            btn_box_data,
            text="📂 Mở Thư Mục Data/",
            font=("Segoe UI", 9, "bold"),
            bg=self.colors["accent"],
            fg="#ffffff",
            bd=0,
            padx=12,
            pady=6,
            command=lambda: os.startfile(str(DATA_DIR)),
            cursor="hand2",
        ).pack(side="left", padx=(0, 8))

        tk.Button(
            btn_box_data,
            text="📥 Tải YouTube Vào Data/",
            font=("Segoe UI", 9, "bold"),
            bg=self.colors["bg_card_hover"],
            fg=self.colors["text_primary"],
            activebackground=self.colors["accent"],
            activeforeground="#ffffff",
            bd=0,
            padx=12,
            pady=6,
            command=lambda: self._navigate_to_download("data"),
            cursor="hand2",
        ).pack(side="left", padx=(0, 8))

        # Card Target/
        card_target = tk.Frame(manage_frame, bg=self.colors["bg_card"], padx=15, pady=15)
        card_target.grid(row=0, column=1, sticky="nsew", padx=(8, 0))

        tk.Label(
            card_target,
            text="🎯 Thư Mục Video Mục Tiêu (Target/)",
            font=("Segoe UI", 11, "bold"),
            bg=self.colors["bg_card"],
            fg=self.colors["text_primary"],
        ).pack(anchor="w")

        tk.Label(
            card_target,
            text="Người dùng copy/paste video cần kiểm tra vi phạm vào thư mục Target/.\nĐặc trưng sẽ được tính toán sẵn để đối soát ngay lập tức khi mở app.",
            font=("Segoe UI", 9),
            justify="left",
            bg=self.colors["bg_card"],
            fg=self.colors["text_secondary"],
        ).pack(anchor="w", pady=(5, 12))

        btn_box_target = tk.Frame(card_target, bg=self.colors["bg_card"])
        btn_box_target.pack(fill="x", pady=(0, 10))

        tk.Button(
            btn_box_target,
            text="📂 Mở Thư Mục Target/",
            font=("Segoe UI", 9, "bold"),
            bg=self.colors["accent"],
            fg="#ffffff",
            bd=0,
            padx=12,
            pady=6,
            command=lambda: os.startfile(str(TARGET_DIR)),
            cursor="hand2",
        ).pack(side="left", padx=(0, 8))

        tk.Button(
            btn_box_target,
            text="📥 Tải YouTube Vào Target/",
            font=("Segoe UI", 9, "bold"),
            bg=self.colors["bg_card_hover"],
            fg=self.colors["text_primary"],
            activebackground=self.colors["accent"],
            activeforeground="#ffffff",
            bd=0,
            padx=12,
            pady=6,
            command=lambda: self._navigate_to_download("target"),
            cursor="hand2",
        ).pack(side="left", padx=(0, 8))

    # ==================== TAB 3: SETTINGS ====================
    def _build_settings_tab(self) -> None:
        panel = tk.Frame(self.tab_settings, bg=self.colors["bg_card"], padx=20, pady=20)
        panel.pack(fill="both", expand=True, pady=10)

        tk.Label(
            panel,
            text="Thông Tin Công Nghệ & Phần Cứng",
            font=("Segoe UI", 11, "bold"),
            bg=self.colors["bg_card"],
            fg=self.colors["text_primary"],
        ).pack(anchor="w", pady=(0, 10))

        info = (
            f"• AI Thị Giác: V-JEPA 2 (Meta AI Spatio-Temporal, 1024-d, Chống Lật/Crop/Watermark)\n"
            f"• AI Âm Thanh: NeuralFP (Neural Audio Fingerprinting, 128-d, Chống Nhiễu/Nén)\n"
            f"• Phần cứng mục tiêu: NVIDIA GeForce RTX 5060 16GB (Adaptive FP16/BF16)\n"
            f"• Cơ chế lưu trữ: Incremental Feature Store (.npz cache per video)\n"
            f"• Thư mục Temp/: {TEMP_DIR}\n"
        )
        tk.Label(
            panel,
            text=info,
            font=("Segoe UI", 9),
            justify="left",
            bg=self.colors["bg_card"],
            fg=self.colors["text_secondary"],
        ).pack(anchor="w", pady=(0, 15))

        btn_clean = tk.Button(
            panel,
            text="🧹 Dọn Dẹp File Tạm Trong Temp/",
            font=("Segoe UI", 9),
            bg=self.colors["warning"],
            fg="#ffffff",
            bd=0,
            padx=14,
            pady=6,
            command=self.on_clean_temp,
            cursor="hand2",
        )
        btn_clean.pack(anchor="w")

    # ==================== ASYNC STARTUP & AUTO-SYNC ====================
    def _initialize_pipeline_and_sync(self) -> None:
        def worker():
            # 1. Khởi tạo pipeline
            self.pipeline = VideoDetectionPipeline()

            # 2. Tự động đồng bộ Data/ và Target/
            def progress_cb(pct: int, msg: str):
                self.root.after(0, lambda: self._update_progress(pct, msg))

            progress_cb(15, "Đang kiểm tra và đồng bộ kho video gốc Data/...")
            new_data, cache_data, refs = self.pipeline.sync_data_repository(progress_cb)

            progress_cb(60, "Đang kiểm tra và đồng bộ thư mục Target/...")
            new_tgt, cache_tgt, tgts = self.pipeline.sync_target_repository(progress_cb)

            self.cached_references = refs
            self.cached_targets = tgts

            self.root.after(0, lambda: self._on_startup_sync_complete(new_data, cache_data, new_tgt, cache_tgt))

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

    def _on_startup_sync_complete(self, new_data: int, cache_data: int, new_tgt: int, cache_tgt: int) -> None:
        self.badge_status.configure(text="Sẵn sàng (Số liệu đã nạp)", bg=self.colors["success"], fg="#ffffff")
        self.status_lbl.configure(text=f"Hoàn tất đồng bộ: Data ({cache_data} sẵn có, {new_data} mới) | Target ({cache_tgt} sẵn có, {new_tgt} mới)")
        self.progress_bar["value"] = 100
        self.refresh_all_views()

    def on_force_sync(self) -> None:
        if self.is_processing or not self.pipeline:
            return

        self._run_task(
            task_func=lambda cb: (
                self.pipeline.sync_data_repository(cb),
                self.pipeline.sync_target_repository(cb),
            ),
            on_success=self._on_force_sync_success,
            title="Đồng bộ kho video Data/ và Target/",
        )

    def _on_force_sync_success(self, res) -> None:
        self.cached_references = self.pipeline.get_cached_references()
        self.cached_targets = self.pipeline.get_cached_targets()
        self.refresh_all_views()
        messagebox.showinfo("Đồng bộ hoàn tất", "Đã cập nhật toàn bộ video mới trong Data/ và Target/!")

    def refresh_all_views(self) -> None:
        # Cập nhật Target Treeview
        for item in self.tree_targets.get_children():
            self.tree_targets.delete(item)

        self.lbl_target_count.configure(text=f"({len(self.cached_targets)} video)")

        for tgt in self.cached_targets:
            m, s = divmod(int(tgt.duration_sec), 60)
            self.tree_targets.insert(
                "",
                "end",
                iid=tgt.video_id,
                values=(tgt.filename, f"{m:02d}:{s:02d}", "Đã có số liệu"),
            )

        # Chọn dòng đầu tiên nếu có
        if self.cached_targets:
            self.tree_targets.selection_set(self.cached_targets[0].video_id)
            self.selected_target_video = self.cached_targets[0]

        # Cập nhật Reference Checklist
        for widget in self.ref_checklist_inner.winfo_children():
            widget.destroy()

        self.ref_check_vars.clear()

        for ref in self.cached_references:
            var = tk.BooleanVar(value=True)
            self.ref_check_vars[ref.video_id] = var

            m, s = divmod(int(ref.duration_sec), 60)
            chk_text = f"{ref.filename} ({m:02d}:{s:02d} - {ref.visual_count} clips)"

            chk = tk.Checkbutton(
                self.ref_checklist_inner,
                text=chk_text,
                variable=var,
                font=("Segoe UI", 9),
                bg=self.colors["bg_input"],
                fg=self.colors["text_primary"],
                selectcolor=self.colors["bg_card"],
                activebackground=self.colors["bg_input"],
                activeforeground="#ffffff",
                anchor="w",
            )
            chk.pack(fill="x", padx=6, pady=2)

        self.stats_summary_lbl.configure(
            text=f"Kho Data: {len(self.cached_references)} video | Target: {len(self.cached_targets)} video"
        )

        self._on_compare_mode_changed()

    def _on_target_selected(self, event) -> None:
        selected_ids = self.tree_targets.selection()
        if not selected_ids:
            return
        vid_id = selected_ids[0]
        for tgt in self.cached_targets:
            if tgt.video_id == vid_id:
                self.selected_target_video = tgt
                self.status_lbl.configure(text=f"Đã chọn Target: {tgt.filename} ({tgt.duration_sec:.1f}s)")
                break

    def _on_compare_mode_changed(self) -> None:
        mode = self.compare_mode_var.get()
        if mode == "all":
            # Disabled or visually muted
            for child in self.ref_list_controls.winfo_children():
                child.configure(state="disabled")
            for child in self.ref_checklist_inner.winfo_children():
                child.configure(state="disabled")
        else:
            for child in self.ref_list_controls.winfo_children():
                child.configure(state="normal")
            for child in self.ref_checklist_inner.winfo_children():
                child.configure(state="normal")

    def _select_all_refs(self) -> None:
        for var in self.ref_check_vars.values():
            var.set(True)

    def _deselect_all_refs(self) -> None:
        for var in self.ref_check_vars.values():
            var.set(False)

    # ==================== INSTANT MATCHING LOGIC ====================
    def on_run_instant_comparison(self) -> None:
        if self.is_processing or not self.pipeline:
            return

        if not self.selected_target_video:
            messagebox.showwarning("Chưa chọn Target", "Vui lòng chọn 1 video mục tiêu trong danh sách Target bên trái.")
            return

        if not self.cached_references:
            messagebox.showwarning("Kho Data rỗng", "Chưa có video nào trong kho Data/ để so sánh.")
            return

        mode = self.compare_mode_var.get()
        selected_ref_ids: List[str] | None = None

        if mode == "selected":
            selected_ref_ids = [vid_id for vid_id, var in self.ref_check_vars.items() if var.get()]
            if not selected_ref_ids:
                messagebox.showwarning("Chưa chọn video tham chiếu", "Vui lòng tích chọn ít nhất 1 video tham chiếu để so sánh.")
                return

        target_v = self.selected_target_video

        def task(cb):
            start_t = time.perf_counter()
            matches = self.pipeline.compare_target_with_references(
                target_video_path=Path(target_v.video_path),
                selected_reference_ids=selected_ref_ids,
                progress_callback=cb,
            )
            elapsed = time.perf_counter() - start_t
            return matches, elapsed

        self._run_task(
            task_func=task,
            on_success=self._on_comparison_finished,
            title=f"Đối soát {target_v.filename}",
        )

    def _on_comparison_finished(self, result) -> None:
        matches, elapsed = result
        self.last_comparison_results = matches

        # Xóa kết quả cũ
        for item in self.tree_matches.get_children():
            self.tree_matches.delete(item)

        target_name = self.selected_target_video.filename if self.selected_target_video else "Target"

        if not matches:
            self.lbl_match_summary.configure(
                text=f"AN TOÀN: Không phát hiện đoạn trùng lặp (Thời gian so sánh: {elapsed*1000:.1f}ms)",
                fg=self.colors["success"],
            )
            self.tree_matches.insert(
                "",
                "end",
                values=(target_name, "Không có trùng khớp", "-", "-", "-", "-", "-", "AN TOÀN"),
            )
            messagebox.showinfo(
                "Kết quả đối soát",
                f"Không tìm thấy đoạn video nào bị sao chép.\n(Thời gian so sánh: {elapsed*1000:.1f}ms)",
            )
            return

        self.lbl_match_summary.configure(
            text=f"PHÁT HIỆN: {len(matches)} đoạn trùng lặp (Thời gian: {elapsed*1000:.1f}ms)",
            fg=self.colors["danger"],
        )

        for m in matches:
            conf_str = f"{m.confidence_score * 100:.1f}%"
            dur_str = f"{m.duration_sec:.1f}s"
            type_label = {
                "multimodal": "Hình ảnh & Âm thanh",
                "visual_only": "Chỉ Hình ảnh",
                "audio_only": "Chỉ Âm thanh",
            }.get(m.match_type, m.match_type)

            self.tree_matches.insert(
                "",
                "end",
                values=(
                    target_name,
                    m.reference_video_id,
                    f"{m._format_time(m.target_start_sec)} - {m._format_time(m.target_end_sec)}",
                    f"{m._format_time(m.ref_start_sec)} - {m._format_time(m.ref_end_sec)}",
                    dur_str,
                    conf_str,
                    type_label,
                    "TRÙNG LẶP",
                ),
            )

        messagebox.showwarning(
            "Phát hiện trùng lặp bản quyền!",
            f"Phát hiện {len(matches)} đoạn trùng lặp giữa '{target_name}' và kho video của chủ sở hữu!\n"
            f"Thời gian tính toán đối soát: {elapsed*1000:.1f}ms",
        )

    def on_export_comparison_results(self) -> None:
        if not self.last_comparison_results:
            messagebox.showinfo("Thông báo", "Chưa có kết quả đối soát nào để xuất.")
            return

        save_p = filedialog.asksaveasfilename(
            defaultextension=".json",
            filetypes=[("JSON File", "*.json")],
            initialfile="ket_qua_doi_soat.json",
        )
        if not save_p:
            return

        target_name = self.selected_target_video.filename if self.selected_target_video else "target"
        data = {
            "target_video": target_name,
            "total_matches": len(self.last_comparison_results),
            "matches": [m.to_dict() for m in self.last_comparison_results],
        }

        with open(save_p, "w", encoding="utf-8") as f:
            json.dump(data, f, indent=2, ensure_ascii=False)

        messagebox.showinfo("Thành công", f"Đã xuất báo cáo ra file:\n{save_p}")

    def on_clean_temp(self) -> None:
        count = clean_temp_dir()
        messagebox.showinfo("Dọn dẹp Temp", f"Đã dọn dẹp {count} file tạm trong thư mục Temp/.")

    def _run_task(self, task_func, on_success, title: str) -> None:
        self.is_processing = True
        self.progress_bar["value"] = 0
        self.status_lbl.configure(text=f"Đang xử lý: {title}...")

        def worker():
            try:
                def update_cb(pct: int, msg: str):
                    self.root.after(0, lambda: self._update_progress(pct, msg))

                result = task_func(update_cb)
                self.root.after(0, lambda: self._on_task_done(on_success, result))
            except Exception as e:
                logger.error(f"Lỗi task: {e}", exc_info=True)
                self.root.after(0, lambda: self._on_task_error(str(e)))

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

    def _update_progress(self, pct: int, msg: str) -> None:
        self.progress_bar["value"] = pct
        self.status_lbl.configure(text=msg)

    def _on_task_done(self, on_success, result) -> None:
        self.is_processing = False
        self.progress_bar["value"] = 100
        self.status_lbl.configure(text="Sẵn sàng.")
        if on_success:
            on_success(result)

    def _on_task_error(self, err: str) -> None:
        self.is_processing = False
        self.progress_bar["value"] = 0
        self.status_lbl.configure(text="Lỗi xảy ra trong quá trình xử lý.")
        messagebox.showerror("Lỗi", f"Lỗi xảy ra:\n{err}")


def launch_gui() -> None:
    root = tk.Tk()
    app = VideoDetectionGUI(root)
    root.mainloop()


if __name__ == "__main__":
    launch_gui()
