//! HitTest 索引模块
//! 
//! 用于将屏幕坐标映射到 DOMNode，支持事件分发和交互。

use std::collections::HashMap;
use iris_layout::dom::DOMNode;

/// 渲染区域（用于 HitTest）
#[derive(Debug, Clone)]
pub struct HitArea {
    /// DOMNode ID
    pub node_id: u64,
    /// 父节点 ID（用于事件冒泡）
    pub parent_id: Option<u64>,
    /// Vue VNode ID（用于事件绑定）
    pub vnode_id: Option<String>,
    /// 事件类型 JSON（如 `{"click":true}`）
    pub event_types: Option<String>,
    /// 边界框（x, y, width, height）
    pub x: f32,
    pub y: f32,
    pub width: f32,
    pub height: f32,
    /// z-index 层级
    pub z_index: i32,
    /// CSS class（用于调试）
    pub class: String,
    /// 元素标签（用于调试）
    pub tag: String,
}

impl HitArea {
    /// 检查点是否在区域内
    pub fn contains(&self, x: f32, y: f32) -> bool {
        x >= self.x && x <= self.x + self.width &&
        y >= self.y && y <= self.y + self.height
    }
    
    /// 获取区域面积
    pub fn area(&self) -> f32 {
        self.width * self.height
    }
}

/// HitTest 索引管理器
#[derive(Debug, Default)]
pub struct HitIndex {
    /// node_id → HitArea 映射
    areas: HashMap<u64, HitArea>,
}

impl HitIndex {
    /// 创建新的 HitTest 索引
    pub fn new() -> Self {
        Self {
            areas: HashMap::new(),
        }
    }
    
    /// 重建整个索引（从 DOM 树）
    pub fn rebuild(&mut self, dom_tree: &DOMNode) {
        self.areas.clear();
        self.build_recursive(dom_tree, 0.0, 0.0, 0);
    }
    
    /// 递归构建索引
    fn build_recursive(
        &mut self,
        node: &DOMNode,
        parent_x: f32,
        parent_y: f32,
        parent_z: i32,
    ) {
        // 获取节点的布局信息
        let (lx, ly, lw, lh) = if let Some(ref layout) = node.layout_signal.get() {
            (layout.x, layout.y, layout.width, layout.height)
        } else if let Some(ref _cache) = node.computed_styles_cache {
            // 回退：尝试从样式缓存推断（不准确，仅用于调试）
            (parent_x, parent_y, 0.0, 0.0)
        } else {
            (parent_x, parent_y, 0.0, 0.0)
        };
        
        // 跳过无效尺寸的元素
        if lw <= 0.0 || lh <= 0.0 {
            // 仍然递归子节点（容器可能有 padding 但无 content）
            for child in &node.children {
                self.build_recursive(child, lx, ly, parent_z);
            }
            return;
        }
        
        // 提取 Vue 特定属性
        let vnode_id = node.attributes.get("_vnodeId").cloned();
        let event_types = node.attributes.get("_eventTypes").cloned();
        
        // 提取 z-index
        let z_index = node.computed_styles()
            .and_then(|styles| styles.get("z-index").cloned())
            .and_then(|z| z.parse::<i32>().ok())
            .unwrap_or(parent_z);
        
        // 提取 class 和 tag
        let class = node.attributes.get("class").cloned().unwrap_or_default();
        let tag = match &node.node_type {
            iris_layout::dom::NodeType::Element(tag) => tag.clone(),
            _ => String::new(),
        };
        
        // 记录 HitArea
        let absolute_x = parent_x + lx;
        let absolute_y = parent_y + ly;
        
        self.areas.insert(node.id, HitArea {
            node_id: node.id,
            parent_id: if node.parent_id != 0 { Some(node.parent_id) } else { None },
            vnode_id,
            event_types,
            x: absolute_x,
            y: absolute_y,
            width: lw,
            height: lh,
            z_index,
            class,
            tag,
        });
        
        // 递归子节点
        for child in &node.children {
            self.build_recursive(child, absolute_x, absolute_y, z_index);
        }
    }
    
    /// HitTest：查找包含指定点的最顶层元素
    pub fn hit_test(&self, x: f32, y: f32) -> Option<&HitArea> {
        // 收集所有包含该点的区域
        let mut candidates: Vec<&HitArea> = self.areas.values()
            .filter(|area| area.contains(x, y))
            .collect();
        
        if candidates.is_empty() {
            return None;
        }
        
        // 按 z-index 降序排序（顶层优先）
        candidates.sort_by(|a, b| b.z_index.cmp(&a.z_index));
        
        // 如果 z-index 相同，选择面积最小的（更精确的匹配）
        if candidates.len() > 1 && candidates[0].z_index == candidates[1].z_index {
            candidates.sort_by(|a, b| a.area().partial_cmp(&b.area()).unwrap_or(std::cmp::Ordering::Equal));
        }
        
        candidates.first().copied()
    }
    
    /// 查找具有指定 vnode_id 的区域
    pub fn find_by_vnode_id(&self, vnode_id: &str) -> Option<&HitArea> {
        self.areas.values()
            .find(|area| area.vnode_id.as_deref() == Some(vnode_id))
    }
    
    /// 查找具有指定 node_id 的区域
    pub fn find_by_node_id(&self, node_id: u64) -> Option<&HitArea> {
        self.areas.get(&node_id)
    }
    
    /// 获取指定 node_id 的 HitArea（find_by_node_id 的别名）
    pub fn get_area(&self, node_id: u64) -> Option<&HitArea> {
        self.find_by_node_id(node_id)
    }
    
    /// 获取所有具有事件处理器的区域
    pub fn get_interactive_areas(&self) -> Vec<&HitArea> {
        self.areas.values()
            .filter(|area| area.event_types.is_some())
            .collect()
    }
    
    /// 获取索引统计信息
    pub fn stats(&self) -> HitIndexStats {
        let total = self.areas.len();
        let interactive = self.areas.values()
            .filter(|area| area.event_types.is_some())
            .count();
        
        HitIndexStats {
            total,
            interactive,
        }
    }
    
    /// 清空索引
    pub fn clear(&mut self) {
        self.areas.clear();
    }
}

/// HitTest 索引统计信息
#[derive(Debug, Clone)]
pub struct HitIndexStats {
    /// 总区域数
    pub total: usize,
    /// 可交互区域数（有事件处理器）
    pub interactive: usize,
}

impl std::fmt::Display for HitIndexStats {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "HitIndex: {} total, {} interactive", self.total, self.interactive)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use iris_layout::dom::NodeType;
    
    fn create_test_dom() -> DOMNode {
        let mut root = DOMNode::new_element("div");
        root.id = 1;
        root.set_attribute("class", "container");
        
        // 设置布局
        let layout = iris_layout::layout::LayoutBox {
            x: 0.0,
            y: 0.0,
            width: 800.0,
            height: 600.0,
        };
        root.layout_signal.set(Some(layout));
        
        // 子元素 1：按钮
        let mut button = DOMNode::new_element("button");
        button.id = 2;
        button.set_attribute("_vnodeId", "42");
        button.set_attribute("_eventTypes", r#"{"click":true}"#);
        button.set_attribute("class", "btn");
        
        let button_layout = iris_layout::layout::LayoutBox {
            x: 100.0,
            y: 100.0,
            width: 200.0,
            height: 50.0,
        };
        button.layout_signal.set(Some(button_layout));
        
        // 子元素 2：文本
        let mut text = DOMNode::new(NodeType::Text("Hello".to_string()));
        text.id = 3;
        
        let text_layout = iris_layout::layout::LayoutBox {
            x: 120.0,
            y: 110.0,
            width: 100.0,
            height: 20.0,
        };
        text.layout_signal.set(Some(text_layout));
        
        button.children.push(text);
        root.children.push(button);
        
        root
    }
    
    #[test]
    fn test_hit_index_build() {
        let dom = create_test_dom();
        let mut index = HitIndex::new();
        index.rebuild(&dom);
        
        assert_eq!(index.areas.len(), 3);
        assert!(index.areas.contains_key(&1));
        assert!(index.areas.contains_key(&2));
        assert!(index.areas.contains_key(&3));
    }
    
    #[test]
    fn test_hit_test_button() {
        let dom = create_test_dom();
        let mut index = HitIndex::new();
        index.rebuild(&dom);
        
        // 点击按钮中心
        let hit = index.hit_test(200.0, 125.0);
        assert!(hit.is_some());
        
        let area = hit.unwrap();
        assert_eq!(area.node_id, 2);
        assert_eq!(area.vnode_id.as_deref(), Some("42"));
        assert!(area.event_types.is_some());
    }
    
    #[test]
    fn test_hit_test_text() {
        let dom = create_test_dom();
        let mut index = HitIndex::new();
        index.rebuild(&dom);
        
        // 点击文本区域
        let hit = index.hit_test(150.0, 115.0);
        assert!(hit.is_some());
        
        // 文本是按钮的子元素，应该在按钮之上
        let area = hit.unwrap();
        assert_eq!(area.node_id, 3);
    }
    
    #[test]
    fn test_hit_test_outside() {
        let dom = create_test_dom();
        let mut index = HitIndex::new();
        index.rebuild(&dom);
        
        // 点击外部区域
        let hit = index.hit_test(900.0, 900.0);
        assert!(hit.is_none());
    }
    
    #[test]
    fn test_find_by_vnode_id() {
        let dom = create_test_dom();
        let mut index = HitIndex::new();
        index.rebuild(&dom);
        
        let area = index.find_by_vnode_id("42");
        assert!(area.is_some());
        assert_eq!(area.unwrap().node_id, 2);
    }
    
    #[test]
    fn test_get_interactive_areas() {
        let dom = create_test_dom();
        let mut index = HitIndex::new();
        index.rebuild(&dom);
        
        let interactive = index.get_interactive_areas();
        assert_eq!(interactive.len(), 1);
        assert_eq!(interactive[0].node_id, 2);
    }
    
    #[test]
    fn test_hit_index_stats() {
        let dom = create_test_dom();
        let mut index = HitIndex::new();
        index.rebuild(&dom);
        
        let stats = index.stats();
        assert_eq!(stats.total, 3);
        assert_eq!(stats.interactive, 1);
    }
}
