"""带配置选项的视频格式转换器 - 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 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: Path | None = None,
        output_format: str | None = None,
        video_codec: str | None = None,
        audio_codec: str | None = None,
        video_bitrate: int | None = None,
        audio_bitrate: int | None = None,
        frame_rate: int | None = None,
        scale_factor: float | None = None,
        crf: int | None = None,
        preset: str | None = None,
        *,
        remove_audio: bool | None = None,
        audio_file: str | None = 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: ConvertWorker | None = 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  # noqa: S404

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

            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:
        """转换完成回调."""
        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()
