//! VideoPlugin — 视频播放引擎
//!
//! 基于 OpenCV 的视频播放，支持状态机驱动、帧变换、过渡效果。

pub mod processor;
pub mod state_machine;

use crate::core::message::{Destination, Envelope, Message, PlayerCommand, PlayerStatusData};
use crate::core::plugin::{Platform, Plugin, PluginContext, PluginInfo};
use crate::core::plugin_ids;
use anyhow::{anyhow, Context, Result};
use opencv::highgui;
use processor::VideoProcessor;
use std::sync::{Arc, Mutex, MutexGuard};
use std::thread::JoinHandle;

pub struct VideoPlugin {
    ctx: Option<PluginContext>,
    processor: Option<Arc<Mutex<VideoProcessor>>>,
    worker: Option<JoinHandle<()>>,
}

impl VideoPlugin {
    pub fn new() -> Self {
        Self {
            ctx: None,
            processor: None,
            worker: None,
        }
    }

    fn processor(&self) -> Result<&Arc<Mutex<VideoProcessor>>> {
        self.processor
            .as_ref()
            .context("video processor is not initialized")
    }

    fn publish_status(&self) {
        let Some(ctx) = &self.ctx else {
            return;
        };
        let Some(processor) = &self.processor else {
            return;
        };

        let status = match processor.lock() {
            Ok(processor) => processor.status(),
            Err(_) => return,
        };

        if let Err(error) = ctx.tx.send(Envelope {
            from: self.id().to_string(),
            to: Destination::Broadcast,
            message: Message::PlayerStatus(status),
        }) {
            eprintln!("[VideoPlugin] failed to publish status: {error}");
        }
    }

    fn publish_state_changed(&self, old_state: Option<String>, new_state: Option<String>) {
        let Some(ctx) = &self.ctx else {
            return;
        };

        let (Some(old_state), Some(new_state)) = (old_state, new_state) else {
            return;
        };

        if old_state == new_state {
            return;
        }

        if let Err(error) = ctx.tx.send(Envelope {
            from: self.id().to_string(),
            to: Destination::Broadcast,
            message: Message::StateChanged {
                old_state,
                new_state,
            },
        }) {
            eprintln!("[VideoPlugin] failed to publish state change: {error}");
        }
    }
}

impl Default for VideoPlugin {
    fn default() -> Self {
        Self::new()
    }
}

impl Plugin for VideoPlugin {
    fn id(&self) -> &str {
        "video"
    }

    fn info(&self) -> PluginInfo {
        PluginInfo {
            name: "Video Player".to_string(),
            version: "0.2.0".to_string(),
            description: "视频播放引擎 (OpenCV)".to_string(),
            platform: Platform::Any,
        }
    }

    fn dependencies(&self) -> Vec<String> {
        vec![]
    }

    fn init(&mut self, ctx: PluginContext) -> Result<()> {
        self.ctx = Some(ctx);
        Ok(())
    }

    fn start(&mut self) -> Result<()> {
        let ctx = self
            .ctx
            .as_ref()
            .context("video plugin context is not initialized")?;

        // 如果是 Live2D 模式，不启动视频播放
        if ctx.config.character.render_type == crate::core::config::RenderType::Live2d {
            println!("[VideoPlugin] Live2D 模式，跳过视频播放启动");
            ctx.tx.send(Envelope {
                from: self.id().to_string(),
                to: Destination::Manager,
                message: Message::PluginReady(self.id().to_string()),
            })?;
            return Ok(());
        }

        let processor = Arc::new(Mutex::new(VideoProcessor::new((*ctx.config).clone())?));
        let worker_processor = Arc::clone(&processor);
        let tx = ctx.tx.clone();
        let handle = std::thread::spawn(move || {
            if let Err(error) = run_processor_loop(worker_processor, tx) {
                eprintln!("[VideoPlugin] playback loop failed: {error}");
            }
        });

        self.processor = Some(processor);
        self.worker = Some(handle);

        ctx.tx.send(Envelope {
            from: self.id().to_string(),
            to: Destination::Manager,
            message: Message::PluginReady(self.id().to_string()),
        })?;

        self.publish_status();
        Ok(())
    }

    fn handle_message(&mut self, msg: Message) -> Result<()> {
        match msg {
            Message::PlayerCommand(command) => {
                let processor = Arc::clone(self.processor()?);
                let mut processor = lock_processor(&processor)?;
                match command {
                    PlayerCommand::Play => {
                        processor.play()?;
                        // 恢复播放时重新获取防息屏锁
                        if let Some(ctx) = &self.ctx {
                            let _ = ctx.tx.send(Envelope {
                                from: self.id().to_string(),
                                to: Destination::Plugin(plugin_ids::SCREEN.to_string()),
                                message: Message::ScreenLockRequest(true),
                            });
                        }
                    }
                    PlayerCommand::Pause => {
                        processor.pause();
                        // 暂停时释放防息屏锁
                        if let Some(ctx) = &self.ctx {
                            let _ = ctx.tx.send(Envelope {
                                from: self.id().to_string(),
                                to: Destination::Plugin(plugin_ids::SCREEN.to_string()),
                                message: Message::ScreenLockRequest(false),
                            });
                        }
                    }
                    PlayerCommand::Next => {
                        processor.next_video()?;
                    }
                    PlayerCommand::Previous => {
                        processor.previous()?;
                    }
                    PlayerCommand::Goto(index) => {
                        processor.goto(index)?;
                    }
                    PlayerCommand::ChangeScene(name) => {
                        processor.change_scene(&name)?;
                    }
                }
                drop(processor);
                self.publish_status();
            }
            Message::Trigger { name, value } => {
                let processor = Arc::clone(self.processor()?);
                let mut processor = lock_processor(&processor)?;
                let old_state = processor.current_state().map(str::to_owned);
                processor.trigger(&name, &value)?;
                let new_state = processor.current_state().map(str::to_owned);
                drop(processor);
                self.publish_state_changed(old_state, new_state);
                self.publish_status();
            }
            Message::ConfigReloaded(config) => {
                // Live2D 模式：停止视频播放，由 Live2DPlugin 接管原生渲染
                if config.character.render_type == crate::core::config::RenderType::Live2d {
                    // 停止视频播放
                    if let Some(old) = self.processor.take() {
                        if let Ok(mut old) = old.lock() {
                            let _ = old.stop();
                        }
                    }
                    if let Some(handle) = self.worker.take() {
                        let _ = handle.join();
                    }
                    // 杀掉残留的 Firefox（如旧方案遗留）
                    let _ = std::process::Command::new("/bin/bash")
                        .arg("-c")
                        .arg("kill -9 $(pgrep firefox-esr) 2>/dev/null")
                        .output();
                    println!("[VideoPlugin] Live2D 模式：视频已停止，由 Live2DPlugin 接管渲染");
                    self.publish_status();
                    return Ok(());
                }

                // 切回视频模式：杀掉残留 Firefox
                let _ = std::process::Command::new("/bin/bash")
                    .arg("-c")
                    .arg("kill -9 $(pgrep firefox-esr) 2>/dev/null")
                    .output();

                let processor = Arc::new(Mutex::new(VideoProcessor::new(config)?));
                if let Some(old) = self.processor.replace(Arc::clone(&processor)) {
                    if let Ok(mut old) = old.lock() {
                        let _ = old.stop();
                    }
                }

                if let Some(handle) = self.worker.take() {
                    let _ = handle.join();
                }

                let worker_processor = Arc::clone(&processor);
                let tx = self
                    .ctx
                    .as_ref()
                    .context("video plugin context is not initialized")?
                    .tx
                    .clone();
                self.worker = Some(std::thread::spawn(move || {
                    if let Err(error) = run_processor_loop(worker_processor, tx) {
                        eprintln!("[VideoPlugin] playback loop failed after reload: {error}");
                    }
                }));
                self.publish_status();
            }
            Message::Shutdown => {
                self.stop()?;
            }
            _ => {}
        }

        Ok(())
    }

    fn stop(&mut self) -> Result<()> {
        if let Some(processor) = &self.processor {
            if let Ok(mut processor) = processor.lock() {
                let _ = processor.stop();
            }
        }

        if let Some(handle) = self.worker.take() {
            let _ = handle.join();
        }

        // 清理残留 Firefox（兼容旧方案）
        let _ = std::process::Command::new("/bin/bash")
            .arg("-c")
            .arg("kill -9 $(pgrep firefox-esr) 2>/dev/null")
            .output();

        self.publish_status();
        Ok(())
    }
}

fn run_processor_loop(
    processor: Arc<Mutex<VideoProcessor>>,
    tx: std::sync::mpsc::Sender<Envelope>,
) -> Result<()> {
    {
        let mut processor = lock_processor(&processor)?;
        processor.start()?;
    }

    publish_processor_status(&tx, &processor)?;

    loop {
        let (outcome, old_state, new_state, old_status, new_status) = {
            let mut processor = lock_processor(&processor)?;
            let old_state = processor.current_state().map(str::to_owned);
            let old_status = processor.status();
            let outcome = processor.step()?;
            let new_state = processor.current_state().map(str::to_owned);
            let new_status = processor.status();
            (outcome, old_state, new_state, old_status, new_status)
        };

        if let Some(frame) = outcome.frame {
            let processor = lock_processor(&processor)?;
            processor.display_frame(&outcome.window_name, &frame)?;
        }

        if old_state != new_state {
            publish_state_changed(&tx, old_state, new_state)?;
        }

        if status_changed(&old_status, &new_status) {
            publish_status_message(&tx, new_status.clone())?;
        }

        if !outcome.keep_running {
            break;
        }

        let key = highgui::wait_key(outcome.delay)?;
        let (old_state, new_state, old_status, new_status) = {
            let mut processor = lock_processor(&processor)?;
            let old_state = processor.current_state().map(str::to_owned);
            let old_status = processor.status();
            processor.handle_key_code(key)?;
            let new_state = processor.current_state().map(str::to_owned);
            let new_status = processor.status();
            (old_state, new_state, old_status, new_status)
        };

        if old_state != new_state {
            publish_state_changed(&tx, old_state, new_state)?;
        }

        if status_changed(&old_status, &new_status) {
            publish_status_message(&tx, new_status.clone())?;
        }

        if !new_status.running {
            break;
        }
    }

    let mut processor = lock_processor(&processor)?;
    processor.stop()
}

fn publish_processor_status(
    tx: &std::sync::mpsc::Sender<Envelope>,
    processor: &Arc<Mutex<VideoProcessor>>,
) -> Result<()> {
    let status = lock_processor(processor)?.status();
    publish_status_message(tx, status)
}

fn publish_status_message(
    tx: &std::sync::mpsc::Sender<Envelope>,
    status: PlayerStatusData,
) -> Result<()> {
    tx.send(Envelope {
        from: "video".to_string(),
        to: Destination::Broadcast,
        message: Message::PlayerStatus(status),
    })?;
    Ok(())
}

fn publish_state_changed(
    tx: &std::sync::mpsc::Sender<Envelope>,
    old_state: Option<String>,
    new_state: Option<String>,
) -> Result<()> {
    let (Some(old_state), Some(new_state)) = (old_state, new_state) else {
        return Ok(());
    };

    if old_state == new_state {
        return Ok(());
    }

    tx.send(Envelope {
        from: "video".to_string(),
        to: Destination::Broadcast,
        message: Message::StateChanged {
            old_state,
            new_state,
        },
    })?;
    Ok(())
}

fn status_changed(old: &PlayerStatusData, new: &PlayerStatusData) -> bool {
    old != new
}

fn lock_processor(
    processor: &Arc<Mutex<VideoProcessor>>,
) -> Result<MutexGuard<'_, VideoProcessor>> {
    processor
        .lock()
        .map_err(|_| anyhow!("video processor lock poisoned"))
}
