"""带配置选项的视频格式转换器 - PySide2 GUI 版本.

本模块提供在不同格式之间转换视频文件的功能.
支持多种输入格式，包括 m4s、mp4、avi、mkv 等.
并提供广泛的配置选项，如输出质量、编解码器等.

命令：videoconv <input_video> [output_dir] [options]
GUI: videoconv-gui
"""

from __future__ import annotations

import sys
from datetime import datetime, timezone
from pathlib import Path
from typing import Optional

from PySide2.QtCore import QThread, Signal
from PySide2.QtGui import QCloseEvent, QFont
from PySide2.QtWidgets import (
    QApplication,
    QCheckBox,
    QComboBox,
    QDoubleSpinBox,
    QFileDialog,
    QGridLayout,
    QGroupBox,
    QHBoxLayout,
    QLabel,
    QLineEdit,
    QMainWindow,
    QMessageBox,
    QProgressBar,
    QPushButton,
    QSpinBox,
    QStatusBar,
    QTextEdit,
    QVBoxLayout,
    QWidget,
)
from pytola_core.logger import get_logger
from pytola_qtstyles import apply_theme

from .cli import VideoConverterRunner, conf

logger = get_logger(__name__)


class ConvertWorker(QThread):
    """后台转换工作线程."""

    progress = Signal(int, str)
    finished = Signal(bool, str)
    log_signal = Signal(str)

    def __init__(
        self,
        input_video: Path,
        output_dir: Optional[Path] = None,
        output_format: Optional[str] = None,
        video_codec: Optional[str] = None,
        audio_codec: Optional[str] = None,
        video_bitrate: Optional[int] = None,
        audio_bitrate: Optional[int] = None,
        frame_rate: Optional[int] = None,
        scale_factor: Optional[float] = None,
        crf: Optional[int] = None,
        preset: Optional[str] = None,
        *,
        remove_audio: Optional[bool] = None,
        audio_file: Optional[str] = None,
    ) -> None:
        super().__init__()
        self.input_video = input_video
        self.output_dir = output_dir
        self.output_format = output_format
        self.video_codec = video_codec
        self.audio_codec = audio_codec
        self.video_bitrate = video_bitrate
        self.audio_bitrate = audio_bitrate
        self.frame_rate = frame_rate
        self.scale_factor = scale_factor
        self.crf = crf
        self.preset = preset
        self.remove_audio = remove_audio
        self.audio_file = audio_file
        self._abort = False

    def run(self) -> None:
        """执行转换任务."""
        try:
            self.log_signal.emit(f"开始转换：{self.input_video.name}")
            self.progress.emit(10, "验证输入文件...")

            runner = VideoConverterRunner(
                input_video=self.input_video,
                output_dir=self.output_dir,
                output_format=self.output_format,
                video_codec=self.video_codec,
                audio_codec=self.audio_codec,
                video_bitrate=self.video_bitrate,
                audio_bitrate=self.audio_bitrate,
                frame_rate=self.frame_rate,
                scale_factor=self.scale_factor,
                crf=self.crf,
                preset=self.preset,
                remove_audio=self.remove_audio,
                audio_file=self.audio_file,
            )

            self.progress.emit(30, "准备转换参数...")

            if not runner._validate_input():
                self.finished.emit(False, "输入文件验证失败")
                return

            self.progress.emit(50, "构建 FFmpeg 命令...")

            cmd = runner._build_ffmpeg_command()
            if not cmd:
                self.finished.emit(False, "构建 FFmpeg 命令失败")
                return

            self.progress.emit(70, "正在转换...")
            self.log_signal.emit(f"FFmpeg 命令：{' '.join(cmd)}")

            success = runner._execute_conversion(cmd)

            self.progress.emit(100 if success else 0, "转换完成" if success else "转换失败")
            self.finished.emit(success, "转换成功" if success else "转换失败")

        except (RuntimeError, ValueError, FileNotFoundError, PermissionError, OSError) as e:
            self.log_signal.emit(f"错误：{e!s}")
            self.finished.emit(False, str(e))

    def abort(self) -> None:
        """中止转换."""
        self._abort = True


class VideoConverterGUI(QMainWindow):
    """视频转换器主窗口."""

    def __init__(self) -> None:
        super().__init__()
        self.setWindowTitle("视频转换器 - VideoConv")
        self.setMinimumSize(720, 600)

        apply_theme(self, "dark")

        self.convert_worker: Optional[ConvertWorker] = None
        self._create_central_widget()
        self._create_status_bar()
        self._create_menu_bar()
        self._load_config_to_ui()
        self._check_ffmpeg_status()

    def _create_central_widget(self) -> None:
        """创建中心控件."""
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QVBoxLayout(central_widget)
        main_layout.setSpacing(10)
        main_layout.setContentsMargins(10, 10, 10, 10)

        self._create_file_selection_group(main_layout)
        self._create_settings_group(main_layout)
        self._create_control_buttons(main_layout)
        self._create_progress_bar(main_layout)
        self._create_log_area(main_layout)

    def _create_file_selection_group(self, parent_layout: QVBoxLayout) -> None:
        """创建文件选择组."""
        group_box = QGroupBox("文件选择")
        layout = QGridLayout()

        layout.addWidget(QLabel("输入文件:"), 0, 0)
        self.input_file_edit = QLineEdit()
        self.input_file_edit.setPlaceholderText("选择要转换的视频文件...")
        self.input_file_edit.setReadOnly(True)
        layout.addWidget(self.input_file_edit, 0, 1)

        self.input_file_btn = QPushButton("浏览...")
        self.input_file_btn.clicked.connect(self.on_select_input_file)
        layout.addWidget(self.input_file_btn, 0, 2)

        layout.addWidget(QLabel("输出目录:"), 1, 0)
        self.output_dir_edit = QLineEdit()
        self.output_dir_edit.setPlaceholderText("可选，留空则与输入文件同目录")
        self.output_dir_edit.setReadOnly(True)
        layout.addWidget(self.output_dir_edit, 1, 1)

        self.output_dir_btn = QPushButton("浏览...")
        self.output_dir_btn.clicked.connect(self.on_select_output_dir)
        layout.addWidget(self.output_dir_btn, 1, 2)

        group_box.setLayout(layout)
        parent_layout.addWidget(group_box)

    def _create_settings_group(self, parent_layout: QVBoxLayout) -> None:
        """创建设置组."""
        group_box = QGroupBox("转换设置")
        layout = QGridLayout()

        layout.addWidget(QLabel("输出格式:"), 0, 0)
        self.format_combo = QComboBox()
        self.format_combo.addItems(sorted(conf.OUTPUT_FORMATS))
        layout.addWidget(self.format_combo, 0, 1)

        layout.addWidget(QLabel("视频编码器:"), 0, 2)
        self.video_codec_combo = QComboBox()
        self.video_codec_combo.addItems(["libx264", "libx265", "libxvid", "mpeg4", "vp9"])
        layout.addWidget(self.video_codec_combo, 0, 3)

        layout.addWidget(QLabel("音频编码器:"), 1, 0)
        self.audio_codec_combo = QComboBox()
        self.audio_codec_combo.addItems(["aac", "mp3", "opus", "vorbis", "ac3"])
        layout.addWidget(self.audio_codec_combo, 1, 1)

        layout.addWidget(QLabel("视频比特率 (kbps):"), 1, 2)
        self.video_bitrate_spin = QSpinBox()
        self.video_bitrate_spin.setRange(100, 50000)
        self.video_bitrate_spin.setValue(conf.VIDEO_BITRATE)
        self.video_bitrate_spin.setSingleStep(100)
        layout.addWidget(self.video_bitrate_spin, 1, 3)

        layout.addWidget(QLabel("音频比特率 (kbps):"), 2, 0)
        self.audio_bitrate_spin = QSpinBox()
        self.audio_bitrate_spin.setRange(64, 512)
        self.audio_bitrate_spin.setValue(conf.AUDIO_BITRATE)
        self.audio_bitrate_spin.setSingleStep(32)
        layout.addWidget(self.audio_bitrate_spin, 2, 1)

        layout.addWidget(QLabel("帧率 (fps):"), 2, 2)
        self.frame_rate_spin = QSpinBox()
        self.frame_rate_spin.setRange(1, 120)
        self.frame_rate_spin.setValue(conf.FRAME_RATE)
        layout.addWidget(self.frame_rate_spin, 2, 3)

        layout.addWidget(QLabel("分辨率缩放:"), 3, 0)
        self.scale_spin = QDoubleSpinBox()
        self.scale_spin.setRange(0.1, 5.0)
        self.scale_spin.setValue(conf.SCALE_FACTOR)
        self.scale_spin.setSingleStep(0.1)
        self.scale_spin.setSuffix("x")
        layout.addWidget(self.scale_spin, 3, 1)

        layout.addWidget(QLabel("CRF 质量 (0-51):"), 3, 2)
        self.crf_spin = QSpinBox()
        self.crf_spin.setRange(0, 51)
        self.crf_spin.setValue(conf.CRF)
        layout.addWidget(self.crf_spin, 3, 3)

        layout.addWidget(QLabel("编码预设:"), 4, 0)
        self.preset_combo = QComboBox()
        self.preset_combo.addItems([
            "ultrafast",
            "superfast",
            "veryfast",
            "faster",
            "fast",
            "medium",
            "slow",
            "slower",
            "veryslow",
        ])
        layout.addWidget(self.preset_combo, 4, 1)

        self.no_audio_check = QCheckBox("移除音频轨道")
        layout.addWidget(self.no_audio_check, 4, 2, 1, 2)

        # 外部音频文件
        layout.addWidget(QLabel("添加音频:"), 5, 0)
        self.audio_file_edit = QLineEdit()
        self.audio_file_edit.setPlaceholderText("可选，选择要合并的外部音频文件...")
        self.audio_file_edit.setReadOnly(True)
        layout.addWidget(self.audio_file_edit, 5, 1, 1, 2)

        self.audio_file_btn = QPushButton("浏览...")
        self.audio_file_btn.clicked.connect(self.on_select_audio_file)
        layout.addWidget(self.audio_file_btn, 5, 3)

        group_box.setLayout(layout)
        parent_layout.addWidget(group_box)

    def _create_control_buttons(self, parent_layout: QVBoxLayout) -> None:
        """创建控制按钮组."""
        button_layout = QHBoxLayout()

        self.start_btn = QPushButton("开始转换")
        self.start_btn.setStyleSheet("QPushButton { font-weight: bold; font-size: 14px; padding: 8px; }")
        self.start_btn.clicked.connect(self.on_start_conversion)
        button_layout.addWidget(self.start_btn)

        self.stop_btn = QPushButton("停止")
        self.stop_btn.setEnabled(False)
        self.stop_btn.clicked.connect(self.on_stop_conversion)
        button_layout.addWidget(self.stop_btn)

        self.reset_btn = QPushButton("重置设置")
        self.reset_btn.clicked.connect(self.on_reset_settings)
        button_layout.addWidget(self.reset_btn)

        parent_layout.addLayout(button_layout)

    def _create_progress_bar(self, parent_layout: QVBoxLayout) -> None:
        """创建进度条."""
        self.progress_bar = QProgressBar()
        self.progress_bar.setRange(0, 100)
        self.progress_bar.setValue(0)
        parent_layout.addWidget(self.progress_bar)

    def _create_log_area(self, parent_layout: QVBoxLayout) -> None:
        """创建日志区域."""
        log_group = QGroupBox("日志输出")
        log_layout = QVBoxLayout()

        self.log_text = QTextEdit()
        self.log_text.setReadOnly(True)
        self.log_text.setMaximumHeight(200)
        log_layout.addWidget(self.log_text)

        log_group.setLayout(log_layout)
        parent_layout.addWidget(log_group)

    def _create_menu_bar(self) -> None:
        """创建菜单栏."""
        menubar = self.menuBar()

        file_menu = menubar.addMenu("文件 (&F)")
        open_action = file_menu.addAction("打开文件 (&O)")
        open_action.triggered.connect(self.on_select_input_file)
        file_menu.addSeparator()
        exit_action = file_menu.addAction("退出 (&X)")
        exit_action.triggered.connect(self.close)

        help_menu = menubar.addMenu("帮助 (&H)")
        about_action = help_menu.addAction("关于 (&A)")
        about_action.triggered.connect(self.show_about)

    def _create_status_bar(self) -> None:
        """创建状态栏."""
        self.statusbar = QStatusBar()
        self.setStatusBar(self.statusbar)

        # FFmpeg 状态标签
        self.ffmpeg_status_label = QLabel()
        self.ffmpeg_status_label.setStyleSheet("QLabel { font-weight: bold; }")
        self.statusbar.addPermanentWidget(self.ffmpeg_status_label, stretch=1)
        font = QFont("Consolas", 6)
        self.ffmpeg_status_label.setFont(font)

        # 转换状态标签
        self.conversion_status_label = QLabel("就绪")
        self.statusbar.addWidget(self.conversion_status_label, stretch=2)

        logger.debug("状态栏已创建")

    def _check_ffmpeg_status(self) -> None:
        """检查 FFmpeg 是否可用并更新状态栏."""
        import subprocess

        try:
            # 运行 ffmpeg -version 检查是否安装
            result = subprocess.run(["ffmpeg", "-version"], capture_output=True, text=True, check=False, timeout=5)

            if result.returncode == 0:
                # 提取版本信息第一行
                version_info = result.stdout.split("\n")[0]
                self.ffmpeg_status_label.setText(f"✓ FFmpeg: {version_info}")
                self.ffmpeg_status_label.setStyleSheet("QLabel { color: #4CAF50; font-weight: bold; }")  # 绿色
                logger.info(f"FFmpeg 已安装：{version_info}")
            else:
                self._set_ffmpeg_not_found("未找到 FFmpeg")

        except FileNotFoundError:
            self._set_ffmpeg_not_found("FFmpeg 未安装")
        except subprocess.TimeoutExpired:
            self._set_ffmpeg_not_found("FFmpeg 响应超时")
        except OSError as e:
            self._set_ffmpeg_not_found(f"FFmpeg 错误：{e!s}")

    def _set_ffmpeg_not_found(self, message: str) -> None:
        """设置 FFmpeg 未找到的状态显示."""
        self.ffmpeg_status_label.setText(f"✗ {message}")
        self.ffmpeg_status_label.setStyleSheet("QLabel { color: #F44336; font-weight: bold; }")  # 红色
        logger.error(message)
        # 在状态栏显示警告消息
        self.statusbar.showMessage(f"警告：{message}，请安装 FFmpeg 并添加到系统 PATH", 10000)

    def _load_config_to_ui(self) -> None:
        """从配置加载设置到 UI."""
        self.format_combo.setCurrentText(conf.OUTPUT_FORMAT)
        self.video_codec_combo.setCurrentText(conf.VIDEO_CODEC)
        self.audio_codec_combo.setCurrentText(conf.AUDIO_CODEC)
        self.video_bitrate_spin.setValue(conf.VIDEO_BITRATE)
        self.audio_bitrate_spin.setValue(conf.AUDIO_BITRATE)
        self.frame_rate_spin.setValue(conf.FRAME_RATE)
        self.scale_spin.setValue(conf.SCALE_FACTOR)
        self.crf_spin.setValue(conf.CRF)
        self.preset_combo.setCurrentText(conf.PRESET)
        self.no_audio_check.setChecked(conf.REMOVE_AUDIO)

    def _save_ui_to_config(self) -> None:
        """保存 UI 设置到配置."""
        conf.OUTPUT_FORMAT = self.format_combo.currentText()
        conf.VIDEO_CODEC = self.video_codec_combo.currentText()
        conf.AUDIO_CODEC = self.audio_codec_combo.currentText()
        conf.VIDEO_BITRATE = self.video_bitrate_spin.value()
        conf.AUDIO_BITRATE = self.audio_bitrate_spin.value()
        conf.FRAME_RATE = self.frame_rate_spin.value()
        conf.SCALE_FACTOR = self.scale_spin.value()
        conf.CRF = self.crf_spin.value()
        conf.PRESET = self.preset_combo.currentText()
        conf.REMOVE_AUDIO = self.no_audio_check.isChecked()

    def on_select_input_file(self) -> None:
        """选择输入文件."""
        file_path, _ = QFileDialog.getOpenFileName(
            self,
            "选择视频文件",
            "",
            "视频文件 (*.mp4 *.avi *.mkv *.mov *.wmv *.flv *.webm *.m4s *.mpeg *.mpg);;所有文件 (*)",
        )
        if file_path:
            self.input_file_edit.setText(file_path)
            self.output_dir_edit.setText(str(Path(file_path).parent))

    def on_select_output_dir(self) -> None:
        """选择输出目录."""
        dir_path = QFileDialog.getExistingDirectory(self, "选择输出目录")
        if dir_path:
            self.output_dir_edit.setText(dir_path)

    def on_select_audio_file(self) -> None:
        """选择外部音频文件."""
        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择音频文件", "", "音频文件 (*.mp3 *.wav *.aac *.flac *.ogg *.m4a *.wma);;所有文件 (*)"
        )
        if file_path:
            self.audio_file_edit.setText(file_path)

    def on_start_conversion(self) -> None:
        """开始转换."""
        input_file = self.input_file_edit.text().strip()
        if not input_file:
            QMessageBox.warning(self, "警告", "请选择要转换的视频文件")
            return

        input_path = Path(input_file)
        output_dir = Path(self.output_dir_edit.text()) if self.output_dir_edit.text().strip() else None

        self._save_ui_to_config()

        self.convert_worker = ConvertWorker(
            input_video=input_path,
            output_dir=output_dir,
            output_format=self.format_combo.currentText(),
            video_codec=self.video_codec_combo.currentText(),
            audio_codec=self.audio_codec_combo.currentText(),
            video_bitrate=self.video_bitrate_spin.value(),
            audio_bitrate=self.audio_bitrate_spin.value(),
            frame_rate=self.frame_rate_spin.value(),
            scale_factor=self.scale_spin.value(),
            crf=self.crf_spin.value(),
            preset=self.preset_combo.currentText(),
            remove_audio=self.no_audio_check.isChecked(),
            audio_file=self.audio_file_edit.text().strip() or None,
        )

        self.convert_worker.progress.connect(self.on_conversion_progress)
        self.convert_worker.finished.connect(self.on_conversion_finished)
        self.convert_worker.log_signal.connect(self.on_log_message)

        self.start_btn.setEnabled(False)
        self.stop_btn.setEnabled(True)
        self.progress_bar.setValue(0)

        # 更新状态栏
        self.conversion_status_label.setText("正在转换...")
        self.conversion_status_label.setStyleSheet("QLabel { color: #2196F3; }")  # 蓝色

        self.convert_worker.start()
        self.log_message("转换任务已启动")

    def on_stop_conversion(self) -> None:
        """停止转换."""
        if self.convert_worker and self.convert_worker.isRunning():
            self.convert_worker.abort()
            self.log_message("用户中止转换")
            # 更新状态栏
            self.conversion_status_label.setText("已中止")
            self.conversion_status_label.setStyleSheet("QLabel { color: #FF9800; }")  # 橙色
            self.on_conversion_finished(success=False, message="用户中止")

    def on_conversion_progress(self, value: int, message: str) -> None:
        """转换进度回调."""
        self.progress_bar.setValue(value)
        self.statusBar().showMessage(message, 5000)
        # 更新状态栏显示详细进度
        self.conversion_status_label.setText(f"进度：{message}")
        self.log_message(f"进度：{message}")

    def on_conversion_finished(self, success: bool, message: str) -> None:  # noqa: FBT001
        """转换完成回调."""
        self.start_btn.setEnabled(True)
        self.stop_btn.setEnabled(False)

        if success:
            QMessageBox.information(self, "成功", f"视频转换成功！\n{message}")
            self.log_message("✓ 转换成功")
            # 更新状态栏为成功状态
            self.conversion_status_label.setText("✓ 转换成功")
            self.conversion_status_label.setStyleSheet("QLabel { color: #4CAF50; }")  # 绿色
        else:
            QMessageBox.warning(self, "失败", f"视频转换失败\n{message}")
            self.log_message(f"✗ 转换失败：{message}")
            # 更新状态栏为失败状态
            self.conversion_status_label.setText(f"✗ 转换失败：{message}")
            self.conversion_status_label.setStyleSheet("QLabel { color: #F44336; }")  # 红色

        self.convert_worker = None

    def on_reset_settings(self) -> None:
        """重置设置到默认值."""
        reply = QMessageBox.question(self, "确认", "确定要重置所有设置为默认值吗？", QMessageBox.Yes | QMessageBox.No)

        if reply == QMessageBox.Yes:
            self._load_config_to_ui()
            self.log_message("设置已重置为默认值")

    def on_log_message(self, message: str) -> None:
        """添加日志消息."""
        timestamp = datetime.now(tz=timezone.utc).strftime("%H:%M:%S")
        self.log_text.append(f"[{timestamp}] {message}")
        scrollbar = self.log_text.verticalScrollBar()
        scrollbar.setValue(scrollbar.maximum())

    def log_message(self, message: str) -> None:
        """记录日志."""
        self.on_log_message(message)

    def show_about(self) -> None:
        """显示关于对话框."""
        QMessageBox.about(
            self,
            "关于视频转换器",
            "<h2>视频转换器 - VideoConv</h2>"
            "<p>版本：0.1.0</p>"
            "<p>一个功能强大的视频格式转换工具</p>"
            "<p>支持多种视频格式之间的转换</p>"
            "<p>© 2026 Pytola Team</p>",
        )

    def closeEvent(self, event: QCloseEvent) -> None:
        """关闭窗口事件."""
        if self.convert_worker and self.convert_worker.isRunning():
            reply = QMessageBox.question(
                self, "确认", "转换正在进行中，确定要退出吗？", QMessageBox.Yes | QMessageBox.No
            )
            if reply == QMessageBox.Yes:
                self.convert_worker.abort()
                event.accept()
            else:
                event.ignore()
        else:
            event.accept()


def main() -> None:
    """运行 GUI 版本的主函数."""
    app = QApplication(sys.argv)
    app.setApplicationName("VideoConv")
    app.setOrganizationName("Pytola")

    font = QFont("Microsoft YaHei UI", 9)
    app.setFont(font)

    window = VideoConverterGUI()
    window.show()

    sys.exit(app.exec_())


if __name__ == "__main__":
    if len(sys.argv) > 1 and sys.argv[1] == "--gui":
        main()
