//! 响应式渲染适配器
//!
//! 将 iris-reactive 响应式布局系统集成到 iris-engine 渲染管线。
//! 实现布局变化自动触发 GPU 渲染更新。

use iris_layout::{
    DOMNode,
    Stylesheet,
    ReactiveLayoutEngine,
    compute_layout_with_effects,
};
use iris_reactive::{create_effect, EffectHandle, batch};
use std::sync::{Arc, Mutex};

/// 响应式渲染适配器
///
/// 桥接响应式布局系统和 GPU 渲染器。
/// 当布局信号变化时，自动标记渲染为脏并触发重绘。
pub struct ReactiveRenderAdapter {
    /// 响应式布局引擎
    layout_engine: Option<ReactiveLayoutEngine>,
    
    /// 布局变化效应句柄
    layout_effect: Option<EffectHandle>,
    
    /// 脏标志回调（通知渲染器重绘）
    dirty_callback: Box<dyn FnMut() + Send>,
    
    /// 当前 DOM 树引用
    dom_tree: Option<DOMNode>,
    
    /// 样式表
    stylesheet: Option<Stylesheet>,
    
    /// 视口宽度
    viewport_width: f32,
    
    /// 视口高度
    viewport_height: f32,
}

impl ReactiveRenderAdapter {
    /// 创建新的响应式渲染适配器
    pub fn new<F>(dirty_callback: F) -> Self
    where
        F: FnMut() + Send + 'static,
    {
        Self {
            layout_engine: None,
            layout_effect: None,
            dirty_callback: Box::new(dirty_callback),
            dom_tree: None,
            stylesheet: None,
            viewport_width: 800.0,
            viewport_height: 600.0,
        }
    }

    /// 初始化响应式布局
    ///
    /// 绑定 DOM 树并创建响应式效应。
    /// 当布局变化时，自动调用 dirty_callback 触发渲染。
    pub fn initialize(
        &mut self,
        dom_tree: &mut DOMNode,
        stylesheet: &Stylesheet,
        viewport_width: f32,
        viewport_height: f32,
    ) {
        self.viewport_width = viewport_width;
        self.viewport_height = viewport_height;
        self.stylesheet = Some(stylesheet.clone());
        
        // 使用响应式布局引擎计算初始布局
        let engine = compute_layout_with_effects(
            dom_tree,
            stylesheet,
            viewport_width,
            viewport_height,
        );
        
        // 克隆 DOM 树引用（用于效应闭包）
        let root_id = dom_tree.id;
        
        // 使用 Arc<Mutex> 包装回调，允许在闭包中调用
        let dirty_callback = Arc::new(Mutex::new(
            std::mem::replace(&mut self.dirty_callback, Box::new(|| {}))
        ));
        
        let dirty_callback_clone = dirty_callback.clone();
        
        let layout_effect = create_effect(move || {
            // 读取根节点的布局信号（建立依赖）
            // 当任何子节点的样式/布局变化时，这里会重新执行
            tracing::debug!(
                "Layout change detected for root node {}",
                root_id
            );
            
            // 触发重绘
            if let Ok(mut callback) = dirty_callback_clone.lock() {
                callback();
            }
        });
        
        // 恢复回调
        if let Ok(_callback) = dirty_callback.lock() {
            // 注意：这里我们保留 Arc<Mutex> 版本，不再提取
            // 因为效应可能会在任何时候调用它
        }
        
        self.layout_effect = Some(layout_effect);
        self.layout_engine = Some(engine);
        self.dom_tree = Some(dom_tree.clone());
        
        tracing::info!("Reactive render adapter initialized");
    }

    /// 更新视口尺寸
    ///
    /// 自动触发响应式布局重算和渲染更新。
    pub fn update_viewport(&mut self, width: f32, height: f32) {
        self.viewport_width = width;
        self.viewport_height = height;
        
        if let Some(engine) = &self.layout_engine {
            // 批量更新视口（自动触发效应）
            batch(|| {
                engine.set_viewport(width, height);
            });
            
            tracing::info!("Viewport updated to {}x{}", width, height);
        }
    }

    /// 获取视口宽度
    pub fn viewport_width(&self) -> f32 {
        self.viewport_width
    }

    /// 获取视口高度
    pub fn viewport_height(&self) -> f32 {
        self.viewport_height
    }

    /// 获取布局引擎引用
    pub fn layout_engine(&self) -> Option<&ReactiveLayoutEngine> {
        self.layout_engine.as_ref()
    }

    /// 手动触发布局重算
    ///
    /// 在 DOM 树结构变化时调用。
    pub fn recompute_layout(&mut self, dom_tree: &mut DOMNode) {
        if let Some(stylesheet) = &self.stylesheet {
            // 重新计算布局（效应会自动更新）
            let _ = compute_layout_with_effects(
                dom_tree,
                stylesheet,
                self.viewport_width,
                self.viewport_height,
            );
            
            // 更新内部 DOM 树引用
            self.dom_tree = Some(dom_tree.clone());
            
            tracing::info!("Layout recomputed after DOM change");
        }
    }

    /// 清理资源
    pub fn dispose(&mut self) {
        if let Some(effect) = self.layout_effect.take() {
            effect.dispose();
        }
        
        if let Some(mut engine) = self.layout_engine.take() {
            engine.dispose();
        }
        
        tracing::info!("Reactive render adapter disposed");
    }
}

impl Drop for ReactiveRenderAdapter {
    fn drop(&mut self) {
        self.dispose();
    }
}

/// 将响应式适配器集成到 RuntimeOrchestrator 的辅助函数
pub fn integrate_reactive_rendering(
    dom_tree: &mut DOMNode,
    stylesheet: &Stylesheet,
    viewport_width: f32,
    viewport_height: f32,
    dirty_callback: impl FnMut() + Send + 'static,
) -> ReactiveRenderAdapter {
    let mut adapter = ReactiveRenderAdapter::new(dirty_callback);
    
    adapter.initialize(
        dom_tree,
        stylesheet,
        viewport_width,
        viewport_height,
    );
    
    adapter
}

#[cfg(test)]
mod tests {
    use super::*;
    use iris_layout::DOMNode;

    #[test]
    fn test_create_adapter() {
        let adapter = ReactiveRenderAdapter::new(|| {});
        
        assert!(adapter.layout_engine.is_none());
        assert!(adapter.layout_effect.is_none());
    }

    #[test]
    fn test_initialize_and_dispose() {
        let mut adapter = ReactiveRenderAdapter::new(|| {});
        
        let mut dom = DOMNode::new_element("div");
        let stylesheet = Stylesheet::new();
        
        adapter.initialize(&mut dom, &stylesheet, 800.0, 600.0);
        
        assert!(adapter.layout_engine.is_some());
        assert!(adapter.layout_effect.is_some());
        
        adapter.dispose();
        
        assert!(adapter.layout_engine.is_none());
        assert!(adapter.layout_effect.is_none());
    }

    #[test]
    fn test_viewport_update() {
        let mut adapter = ReactiveRenderAdapter::new(|| {});
        
        let mut dom = DOMNode::new_element("div");
        let stylesheet = Stylesheet::new();
        
        adapter.initialize(&mut dom, &stylesheet, 800.0, 600.0);
        adapter.update_viewport(1024.0, 768.0);
        
        assert_eq!(adapter.viewport_width(), 1024.0);
        assert_eq!(adapter.viewport_height(), 768.0);
    }
}
