//! Perception engine: infer semantic roles and page intent from DOM structure

use crate::dom::views::{EnhancedDOMTreeNode, PageIntent, SemanticRole};

/// Configuration for the perception engine
#[derive(Debug, Clone)]
pub struct PerceptionConfig {
    /// Whether to enable semantic role inference
    pub enable_role_inference: bool,
    /// Whether to enable page intent classification
    pub enable_page_intent: bool,
    /// Whether to generate affordance descriptions
    pub enable_affordances: bool,
    /// Minimum confidence threshold for emitting a semantic role
    pub confidence_threshold: f32,
}

impl Default for PerceptionConfig {
    fn default() -> Self {
        Self {
            enable_role_inference: true,
            enable_page_intent: true,
            enable_affordances: true,
            confidence_threshold: 0.5,
        }
    }
}

/// Engine that enriches DOM trees with semantic understanding
#[derive(Debug, Clone)]
pub struct PerceptionEngine {
    config: PerceptionConfig,
}

impl PerceptionEngine {
    /// Create a new perception engine with default config
    pub fn new() -> Self {
        Self {
            config: PerceptionConfig::default(),
        }
    }

    /// Create a new perception engine with custom config
    pub fn with_config(config: PerceptionConfig) -> Self {
        Self { config }
    }

    /// Run perception on a DOM tree, mutating nodes in place with semantic annotations
    pub fn perceive(&self, root: &mut EnhancedDOMTreeNode) -> (Option<PageIntent>, f32) {
        if self.config.enable_role_inference {
            self.infer_node_roles(root);
        }

        if self.config.enable_page_intent {
            let (intent, confidence) = self.infer_page_intent(root);
            (Some(intent), confidence)
        } else {
            (None, 0.0)
        }
    }

    /// Recursively infer semantic roles for all nodes in the tree
    fn infer_node_roles(&self, node: &mut EnhancedDOMTreeNode) {
        let (role, confidence, affordance) = self.classify_node(node);
        node.semantic_role = role;
        node.semantic_confidence = confidence;

        // Store affordance in node_value if present (for serializer pickup)
        if let Some(aff) = &affordance {
            // We don't have a dedicated affordance field on EnhancedDOMTreeNode,
            // so we encode it as a pseudo-attribute for now
            node.attributes.insert("__affordance".to_string(), aff.clone());
        }

        // Recurse into children
        if let Some(ref mut children) = node.children_nodes {
            for child in children {
                self.infer_node_roles(child);
            }
        }

        // Recurse into shadow roots
        if let Some(ref mut shadow_roots) = node.shadow_roots {
            for shadow in shadow_roots {
                self.infer_node_roles(shadow);
            }
        }

        // Recurse into content document (iframe)
        if let Some(ref mut content_doc) = node.content_document {
            self.infer_node_roles(content_doc);
        }
    }

    /// Classify a single node into a semantic role
    fn classify_node(
        &self,
        node: &EnhancedDOMTreeNode,
    ) -> (Option<SemanticRole>, f32, Option<String>) {
        if node.node_type != crate::dom::views::NodeType::ElementNode {
            return (None, 0.0, None);
        }

        let tag = node.tag_name();
        let attrs = &node.attributes;
        let text = node.node_value.trim().to_lowercase();
        let role_attr = attrs.get("role").map(|s| s.as_str());

        // ── Search form detection ───────────────────────────────
        if tag == "form" {
            let has_search_input = attrs.get("action").map(|a| a.contains("search")).unwrap_or(false)
                || attrs.get("class").map(|c| c.to_lowercase().contains("search")).unwrap_or(false)
                || attrs.get("id").map(|i| i.to_lowercase().contains("search")).unwrap_or(false);

            let has_search_button = node.children_nodes.as_ref().map(|children| {
                children.iter().any(|c| {
                    let ctag = c.tag_name();
                    let ctext = c.node_value.to_lowercase();
                    (ctag == "button" || ctag == "input")
                        && (ctext.contains("search")
                            || c.attributes.get("value").map(|v| v.to_lowercase().contains("search")).unwrap_or(false)
                            || c.attributes.get("aria-label").map(|v| v.to_lowercase().contains("search")).unwrap_or(false))
                })
            }).unwrap_or(false);

            if has_search_input || has_search_button {
                return (
                    Some(SemanticRole::SearchForm),
                    0.85,
                    Some("Submit a search query".to_string()),
                );
            }
        }

        // ── Login form detection ─────────────────────────────────
        if tag == "form" || tag == "div" {
            let has_password = node.children_nodes.as_ref().map(|children| {
                children.iter().any(|c| {
                    c.attributes.get("type").map(|t| t == "password").unwrap_or(false)
                })
            }).unwrap_or(false);

            let has_login_text = text.contains("login")
                || text.contains("sign in")
                || text.contains("log in")
                || attrs.get("class").map(|c| c.to_lowercase().contains("login")).unwrap_or(false)
                || attrs.get("id").map(|i| i.to_lowercase().contains("login")).unwrap_or(false);

            if has_password || has_login_text {
                return (
                    Some(SemanticRole::LoginForm),
                    0.9,
                    Some("Authenticate with username and password".to_string()),
                );
            }
        }

        // ── Registration form detection ──────────────────────────
        if tag == "form" || tag == "div" {
            let has_register_text = text.contains("register")
                || text.contains("sign up")
                || text.contains("create account")
                || attrs.get("class").map(|c| c.to_lowercase().contains("register")).unwrap_or(false)
                || attrs.get("id").map(|i| i.to_lowercase().contains("register")).unwrap_or(false);

            if has_register_text {
                return (
                    Some(SemanticRole::RegistrationForm),
                    0.8,
                    Some("Create a new account".to_string()),
                );
            }
        }

        // ── Navigation detection ─────────────────────────────────
        if tag == "nav"
            || role_attr == Some("navigation")
            || attrs.get("class").map(|c| c.to_lowercase().contains("nav")).unwrap_or(false)
            || attrs.get("id").map(|i| i.to_lowercase().contains("nav")).unwrap_or(false)
        {
            return (
                Some(SemanticRole::Navigation),
                0.85,
                Some("Navigate to another page".to_string()),
            );
        }

        // ── Pagination detection ─────────────────────────────────
        if (text.contains("page")
            || text.contains("next")
            || text.contains("previous")
            || text.contains("«")
            || text.contains("»")
            || attrs.get("class").map(|c| {
                let c = c.to_lowercase();
                c.contains("paginat") || c.contains("pager")
            }).unwrap_or(false)
            || attrs.get("aria-label").map(|l| l.to_lowercase().contains("page")).unwrap_or(false))
            && (tag == "a" || tag == "button" || tag == "li" || tag == "span") {
                return (
                    Some(SemanticRole::Pagination),
                    0.7,
                    Some("Navigate to another page of results".to_string()),
                );
            }

        // ── Product card detection ───────────────────────────────
        if (attrs.get("class").map(|c| {
            let c = c.to_lowercase();
            c.contains("product") || c.contains("item") || c.contains("card")
        }).unwrap_or(false)
            || attrs.get("data-product-id").is_some()
            || attrs.get("data-sku").is_some())
            && (tag == "div" || tag == "article" || tag == "li") {
                return (
                    Some(SemanticRole::ProductCard),
                    0.65,
                    Some("View product details".to_string()),
                );
            }

        // ── Article detection ────────────────────────────────────
        if tag == "article"
            || role_attr == Some("article")
            || attrs.get("class").map(|c| c.to_lowercase().contains("article")).unwrap_or(false)
            || attrs.get("itemtype").map(|t| t.contains("Article")).unwrap_or(false)
        {
            return (
                Some(SemanticRole::Article),
                0.85,
                Some("Read article content".to_string()),
            );
        }

        // ── Filter / sort detection ────────────────────────────────
        if attrs.get("class").map(|c| {
            let c = c.to_lowercase();
            c.contains("filter") || c.contains("sort") || c.contains("facet")
        }).unwrap_or(false)
            || attrs.get("id").map(|i| {
                let i = i.to_lowercase();
                i.contains("filter") || i.contains("sort")
            }).unwrap_or(false)
        {
            return (
                Some(SemanticRole::FilterPanel),
                0.7,
                Some("Filter or sort results".to_string()),
            );
        }

        // ── CAPTCHA detection ────────────────────────────────────
        if text.contains("captcha")
            || text.contains("i'm not a robot")
            || text.contains("recaptcha")
            || attrs.get("class").map(|c| c.to_lowercase().contains("captcha")).unwrap_or(false)
            || attrs.get("id").map(|i| i.to_lowercase().contains("captcha")).unwrap_or(false)
        {
            return (
                Some(SemanticRole::Captcha),
                0.95,
                Some("Complete verification challenge".to_string()),
            );
        }

        // ── Cookie consent detection ─────────────────────────────
        if (text.contains("cookie")
            || text.contains("privacy")
            || attrs.get("class").map(|c| {
                let c = c.to_lowercase();
                c.contains("cookie") || c.contains("consent") || c.contains("gdpr")
            }).unwrap_or(false)
            || attrs.get("id").map(|i| {
                let i = i.to_lowercase();
                i.contains("cookie") || i.contains("consent")
            }).unwrap_or(false))
            && (text.contains("accept") || text.contains("agree") || text.contains("consent")) {
                return (
                    Some(SemanticRole::CookieConsent),
                    0.8,
                    Some("Accept or manage cookie consent".to_string()),
                );
            }

        // ── Advertisement detection ──────────────────────────────
        if tag == "iframe"
            && (attrs.get("src").map(|s| {
                let s = s.to_lowercase();
                s.contains("ad") || s.contains("doubleclick") || s.contains("googleads")
            }).unwrap_or(false)
                || attrs.get("id").map(|i| i.to_lowercase().contains("ad")).unwrap_or(false)
                || attrs.get("class").map(|c| c.to_lowercase().contains("ad")).unwrap_or(false))
            {
                return (
                    Some(SemanticRole::Advertisement),
                    0.75,
                    None,
                );
            }

        // ── Button classification ────────────────────────────────
        if tag == "button"
            || (tag == "input" && attrs.get("type").map(|t| t == "submit" || t == "button").unwrap_or(false))
            || role_attr == Some("button")
        {
            let btn_text = text.clone();
            let val = attrs.get("value").map(|v| v.to_lowercase()).unwrap_or_default();

            if btn_text.contains("submit")
                || btn_text.contains("save")
                || btn_text.contains("continue")
                || val.contains("submit")
                || val.contains("save")
            {
                return (
                    Some(SemanticRole::SubmitButton),
                    0.8,
                    Some("Submit the form".to_string()),
                );
            }

            if btn_text.contains("search")
                || btn_text.contains("find")
                || val.contains("search")
            {
                return (
                    Some(SemanticRole::PrimaryAction),
                    0.75,
                    Some("Execute search".to_string()),
                );
            }

            if btn_text.contains("buy")
                || btn_text.contains("purchase")
                || btn_text.contains("add to cart")
                || btn_text.contains("checkout")
            {
                return (
                    Some(SemanticRole::PrimaryAction),
                    0.8,
                    Some("Complete purchase".to_string()),
                );
            }

            // Default: secondary action
            return (
                Some(SemanticRole::SecondaryAction),
                0.5,
                Some("Perform an action".to_string()),
            );
        }

        // ── Input classification ─────────────────────────────────
        if tag == "input" || tag == "textarea" {
            let input_type = attrs.get("type").map(|t| t.as_str()).unwrap_or("text");

            match input_type {
                "password" => {
                    return (
                        Some(SemanticRole::TextInput),
                        0.9,
                        Some("Enter password".to_string()),
                    )
                }
                "email" => {
                    return (
                        Some(SemanticRole::TextInput),
                        0.85,
                        Some("Enter email address".to_string()),
                    )
                }
                "search" => {
                    return (
                        Some(SemanticRole::SearchForm),
                        0.9,
                        Some("Enter search query".to_string()),
                    )
                }
                "file" => {
                    return (
                        Some(SemanticRole::FileUpload),
                        0.9,
                        Some("Upload a file".to_string()),
                    )
                }
                "date" | "datetime-local" | "time" => {
                    return (
                        Some(SemanticRole::DatePicker),
                        0.85,
                        Some("Select a date/time".to_string()),
                    )
                }
                "checkbox" | "radio" => {
                    return (
                        Some(SemanticRole::ToggleGroup),
                        0.8,
                        Some("Select an option".to_string()),
                    )
                }
                _ => {
                    let placeholder = attrs.get("placeholder").map(|p| p.to_lowercase()).unwrap_or_default();
                    let name = attrs.get("name").map(|n| n.to_lowercase()).unwrap_or_default();
                    let aria = attrs.get("aria-label").map(|a| a.to_lowercase()).unwrap_or_default();

                    let purpose = if placeholder.contains("search") || name.contains("search") || aria.contains("search") {
                        Some("Enter search query".to_string())
                    } else if placeholder.contains("email") || name.contains("email") || aria.contains("email") {
                        Some("Enter email address".to_string())
                    } else if placeholder.contains("password") || name.contains("password") || aria.contains("password") {
                        Some("Enter password".to_string())
                    } else if placeholder.contains("username") || name.contains("username") || name.contains("user") || aria.contains("username") {
                        Some("Enter username".to_string())
                    } else {
                        Some("Enter text".to_string())
                    };

                    return (
                        Some(SemanticRole::TextInput),
                        0.6,
                        purpose,
                    );
                }
            }
        }

        // ── Select / dropdown ────────────────────────────────────
        if tag == "select" {
            return (
                Some(SemanticRole::Dropdown),
                0.85,
                Some("Select from dropdown".to_string()),
            );
        }

        // ── Structural elements ──────────────────────────────────
        if tag == "header" || role_attr == Some("banner") {
            return (Some(SemanticRole::Header), 0.9, None);
        }
        if tag == "footer" || role_attr == Some("contentinfo") {
            return (Some(SemanticRole::Footer), 0.9, None);
        }
        if tag == "aside" || role_attr == Some("complementary") {
            return (Some(SemanticRole::Sidebar), 0.85, None);
        }
        if tag == "main" || role_attr == Some("main") {
            return (Some(SemanticRole::MainContent), 0.9, None);
        }

        (None, 0.0, None)
    }

    /// Infer page-level intent from the overall DOM structure
    fn infer_page_intent(&self, root: &EnhancedDOMTreeNode) -> (PageIntent, f32) {
        let mut scores: std::collections::HashMap<PageIntent, f32> = std::collections::HashMap::new();

        self.collect_intent_signals(root, &mut scores);

        // Find highest scoring intent
        let mut best_intent = PageIntent::Unknown;
        let mut best_score = 0.0f32;

        for (intent, score) in scores {
            if score > best_score {
                best_score = score;
                best_intent = intent;
            }
        }

        // Normalize confidence
        let confidence = if best_score > 0.0 {
            (best_score / (best_score + 1.0)).min(0.99)
        } else {
            0.0
        };

        (best_intent, confidence)
    }

    /// Recursively collect page intent signals from node roles
    fn collect_intent_signals(
        &self,
        node: &EnhancedDOMTreeNode,
        scores: &mut std::collections::HashMap<PageIntent, f32>,
    ) {
        if let Some(ref role) = node.semantic_role {
            match role {
                SemanticRole::LoginForm => {
                    *scores.entry(PageIntent::Login).or_insert(0.0) += 2.0;
                }
                SemanticRole::SearchForm => {
                    *scores.entry(PageIntent::SearchResults).or_insert(0.0) += 0.5;
                    *scores.entry(PageIntent::Landing).or_insert(0.0) += 0.3;
                }
                SemanticRole::ProductCard => {
                    *scores.entry(PageIntent::ProductListing).or_insert(0.0) += 1.0;
                }
                SemanticRole::Article => {
                    *scores.entry(PageIntent::Article).or_insert(0.0) += 1.5;
                }
                SemanticRole::Captcha => {
                    *scores.entry(PageIntent::Captcha).or_insert(0.0) += 3.0;
                }
                SemanticRole::CookieConsent => {
                    *scores.entry(PageIntent::CookieConsent).or_insert(0.0) += 2.0;
                }
                SemanticRole::SubmitButton => {
                    // If we see a checkout-like button, boost checkout score
                    let text = node.node_value.to_lowercase();
                    if text.contains("checkout") || text.contains("pay") || text.contains("purchase") {
                        *scores.entry(PageIntent::Checkout).or_insert(0.0) += 1.5;
                    }
                }
                _ => {}
            }
        }

        // Check URL/title hints from document-level attributes if present
        if let Some(title) = node.attributes.get("__page_title") {
            let title = title.to_lowercase();
            if title.contains("login") || title.contains("sign in") {
                *scores.entry(PageIntent::Login).or_insert(0.0) += 1.0;
            }
            if title.contains("search results") || title.contains("results for") {
                *scores.entry(PageIntent::SearchResults).or_insert(0.0) += 1.5;
            }
            if title.contains("product") && title.contains("detail") {
                *scores.entry(PageIntent::ProductDetail).or_insert(0.0) += 1.0;
            }
            if title.contains("404") || title.contains("not found") || title.contains("error") {
                *scores.entry(PageIntent::ErrorPage).or_insert(0.0) += 2.0;
            }
        }

        if let Some(ref children) = node.children_nodes {
            for child in children {
                self.collect_intent_signals(child, scores);
            }
        }
        if let Some(ref shadow_roots) = node.shadow_roots {
            for shadow in shadow_roots {
                self.collect_intent_signals(shadow, scores);
            }
        }
        if let Some(ref content_doc) = node.content_document {
            self.collect_intent_signals(content_doc, scores);
        }
    }
}

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