//! BlePlugin — BLE 配网服务
//!
//! 通过 D-Bus 与 BlueZ 交互，注册 GATT 服务和 LE Advertisement。
//! 注意：Advertisement 不可同时 Includes["local-name"] + LocalName 属性
//! （BlueZ 会报 Failed to parse advertisement）。

mod gatt;

use crate::core::message::Message;
use crate::core::plugin::{Platform, Plugin, PluginContext, PluginInfo};
use anyhow::{anyhow, Context, Result};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc;
use std::sync::Arc;
use std::thread::{self, JoinHandle};

pub struct BlePlugin {
    ctx: Option<PluginContext>,
    control_tx: Option<mpsc::Sender<gatt::BleControl>>,
    stop: Arc<AtomicBool>,
    worker: Option<JoinHandle<Result<()>>>,
}

impl BlePlugin {
    pub fn new() -> Self {
        Self {
            ctx: None,
            control_tx: None,
            stop: Arc::new(AtomicBool::new(false)),
            worker: None,
        }
    }
}

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

impl Plugin for BlePlugin {
    fn id(&self) -> &str {
        "ble"
    }

    fn info(&self) -> PluginInfo {
        PluginInfo {
            name: "BLE Provisioning".to_string(),
            version: "0.2.0".to_string(),
            description: "BLE GATT WiFi 配网 (D-Bus BlueZ)".to_string(),
            platform: Platform::Linux,
        }
    }

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

    fn init(&mut self, ctx: PluginContext) -> Result<()> {
        self.stop.store(false, Ordering::SeqCst);
        self.ctx = Some(ctx);
        self.control_tx = None;
        Ok(())
    }

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

        self.stop.store(false, Ordering::SeqCst);

        if !ctx.config.ble.enabled {
            return Ok(());
        }

        let device_name = ctx.config.ble.device_name.clone();
        let tx = ctx.tx.clone();
        let stop = Arc::clone(&self.stop);
        let (control_tx, control_rx) = mpsc::channel();

        self.control_tx = Some(control_tx);

        self.worker = Some(thread::spawn(move || {
            // 自动重连循环：BLE 服务出错后等待 3 秒重试
            loop {
                if stop.load(Ordering::SeqCst) {
                    return Ok(());
                }
                let result =
                    gatt::run_ble_service(device_name.clone(), tx.clone(), &control_rx, &stop);
                match result {
                    Ok(()) => return Ok(()),
                    Err(ref error) => {
                        if stop.load(Ordering::SeqCst) {
                            return Ok(());
                        }
                        // BlueZ 残留状态（database / advertisement）需要更长冷却时间
                        let err_text = format!("{error:#}");
                        let wait_secs = if err_text.contains("database")
                            || err_text.contains("parse advertisement")
                            || err_text.contains("Already Exists")
                        {
                            8
                        } else {
                            3
                        };
                        eprintln!(
                            "[BlePlugin] worker error: {err_text}, restarting in {wait_secs} seconds..."
                        );
                        for _ in 0..(wait_secs * 10) {
                            if stop.load(Ordering::SeqCst) {
                                return Ok(());
                            }
                            thread::sleep(std::time::Duration::from_millis(100));
                        }
                    }
                }
            }
        }));
        Ok(())
    }

    fn handle_message(&mut self, msg: Message) -> Result<()> {
        match msg {
            Message::Shutdown => {
                self.stop.store(true, Ordering::SeqCst);
            }
            Message::WifiResult(payload) => {
                if let Some(control_tx) = &self.control_tx {
                    let _ = control_tx.send(gatt::BleControl::UpdateStatus(payload));
                }
            }
            _ => {}
        }

        Ok(())
    }

    fn stop(&mut self) -> Result<()> {
        self.stop.store(true, Ordering::SeqCst);
        self.control_tx = None;

        if let Some(worker) = self.worker.take() {
            worker
                .join()
                .map_err(|_| anyhow!("BLE worker thread panicked"))??;
        }

        Ok(())
    }
}
