//! Hover information provider

use tower_lsp::lsp_types::{
    Hover,
    HoverContents,
    MarkupContent,
    MarkupKind,
};

use crate::{
    lsp::document::Document,
    resolve::names::DeclKind,
    syntax::{
        ast::{
            Declaration,
            Value,
        },
        lexer::{
            Lexer,
            Token,
        },
    },
};

/// Get hover information at a position
pub fn get_hover_info(text: &str, line: usize, character: usize) -> Option<Hover> {
    // Calculate absolute byte offset from line/character position
    let mut byte_offset = 0;
    let mut found_line = false;

    for (current_line, line_text) in text.lines().enumerate() {
        if current_line == line {
            found_line = true;

            // Check if character position is beyond the line
            let line_char_count = line_text.chars().count();
            if character >= line_char_count {
                return None;
            }

            // Add the character offset (assuming UTF-8)
            for (char_count, (byte_pos, _)) in line_text.char_indices().enumerate() {
                if char_count == character {
                    byte_offset += byte_pos;
                    break;
                }
            }
            break;
        }
        byte_offset += line_text.len() + 1; // +1 for newline
    }

    // If line was not found, return None
    if !found_line {
        return None;
    }

    // Tokenize and find the token at the cursor position
    let lexer = Lexer::new(text);
    let mut target_token = None;

    for (offset, token, end) in lexer {
        if offset <= byte_offset && byte_offset < end {
            target_token = Some(token);
            break;
        }
    }

    let token = target_token?;

    // Generate hover info based on the token
    let content = get_token_documentation(&token)?;

    Some(Hover {
        contents: HoverContents::Markup(MarkupContent {
            kind: MarkupKind::Markdown,
            value: content.to_string(),
        }),
        range: None,
    })
}

/// Get documentation for a token
fn get_token_documentation(token: &Token) -> Option<&'static str> {
    match token {
        Token::Character => Some("**character** - Defines a character entity\n\nSyntax: `character Name { ... }`"),
        Token::Template => Some("**template** - Defines a reusable field template\n\nSyntax: `template Name { ... }`"),
        Token::LifeArc => Some("**life_arc** - Defines a state machine for character development\n\nSyntax: `life_arc Name { ... }`"),
        Token::Schedule => Some("**schedule** - Defines a daily schedule or routine\n\nSyntax: `schedule Name { ... }`"),
        Token::Behavior => Some("**behavior** - Defines a behavior tree for AI\n\nSyntax: `behavior Name { ... }`"),
        Token::Institution => Some("**institution** - Defines an organization or group\n\nSyntax: `institution Name { ... }`"),
        Token::Relationship => Some("**relationship** - Defines a multi-party relationship\n\nSyntax: `relationship Name { ... }`"),
        Token::Location => Some("**location** - Defines a place or setting\n\nSyntax: `location Name { ... }`"),
        Token::Species => Some("**species** - Defines a species with templates\n\nSyntax: `species Name { ... }`"),
        Token::Use => Some("**use** - Imports declarations from other files\n\nSyntax: `use path::to::item;`"),
        Token::From => Some("**from** - Applies templates to a character\n\nSyntax: `character Name from Template { ... }`"),
        Token::Include => Some("**include** - Includes another template\n\nSyntax: `include TemplateName`"),
        Token::State => Some("**state** - Defines a state in a life arc\n\nSyntax: `state name { ... }`"),
        Token::On => Some("**on** - Defines a transition or enter handler\n\nSyntax: `on condition -> target` or `on enter { ... }`"),
        Token::Strict => Some("**strict** - Enforces that a template only accepts defined fields"),
        _ => None,
    }
}

/// Get semantic hover information for symbols
pub fn get_semantic_hover_info(doc: &Document, line: usize, character: usize) -> Option<Hover> {
    let ast = doc.ast.as_ref()?;

    // Calculate absolute byte offset from line/character position
    let mut byte_offset = 0;
    let mut found_line = false;

    for (current_line, line_text) in doc.text.lines().enumerate() {
        if current_line == line {
            found_line = true;

            // Check if character position is beyond the line
            let line_char_count = line_text.chars().count();
            if character >= line_char_count {
                return None;
            }

            for (char_count, (byte_pos, _)) in line_text.char_indices().enumerate() {
                if char_count == character {
                    byte_offset += byte_pos;
                    break;
                }
            }
            break;
        }
        byte_offset += line_text.len() + 1; // +1 for newline
    }

    if !found_line {
        return None;
    }

    // Tokenize and find the identifier at the cursor position
    let lexer = Lexer::new(&doc.text);
    let mut target_ident = None;

    for (offset, token, end) in lexer {
        if offset <= byte_offset && byte_offset < end {
            if let Token::Ident(name) = token {
                target_ident = Some(name);
            }
            break;
        }
    }

    let word = target_ident?;

    // Look up the symbol in the name table
    let symbol_info = doc.name_table.lookup(std::slice::from_ref(&word))?;

    // Find the declaration in the AST
    for decl in &ast.declarations {
        let decl_name = get_declaration_name(decl);
        if decl_name.as_deref() == Some(word.as_str()) {
            return Some(format_declaration_hover(decl, &symbol_info.kind));
        }
    }

    None
}

/// Extract the name from a declaration
fn get_declaration_name(decl: &Declaration) -> Option<String> {
    match decl {
        | Declaration::Character(c) => Some(c.name.clone()),
        | Declaration::Template(t) => Some(t.name.clone()),
        | Declaration::Species(s) => Some(s.name.clone()),
        | Declaration::Location(l) => Some(l.name.clone()),
        | Declaration::Institution(i) => Some(i.name.clone()),
        | Declaration::Relationship(r) => Some(r.name.clone()),
        | Declaration::LifeArc(la) => Some(la.name.clone()),
        | Declaration::Schedule(s) => Some(s.name.clone()),
        | Declaration::Behavior(b) => Some(b.name.clone()),
        | Declaration::Use(_) => None,
        | Declaration::Concept(_) | Declaration::SubConcept(_) | Declaration::Definition(_) => None, /* TODO: Implement hover for type system */
    }
}

/// Format hover information for a declaration
fn format_declaration_hover(decl: &Declaration, _kind: &DeclKind) -> Hover {
    let content = match decl {
        | Declaration::Character(c) => format_character_hover(c),
        | Declaration::Template(t) => format_template_hover(t),
        | Declaration::Species(s) => format_species_hover(s),
        | Declaration::Location(l) => format_location_hover(l),
        | Declaration::Institution(i) => format_institution_hover(i),
        | Declaration::Relationship(r) => format_relationship_hover(r),
        | Declaration::LifeArc(la) => format_life_arc_hover(la),
        | Declaration::Schedule(s) => format_schedule_hover(s),
        | Declaration::Behavior(b) => format_behavior_hover(b),
        | Declaration::Use(_) => "**use** declaration".to_string(),
        | Declaration::Concept(_) => "**concept** declaration".to_string(),
        | Declaration::SubConcept(_) => "**sub_concept** declaration".to_string(),
        | Declaration::Definition(_) => "**definition** declaration".to_string(),
    };

    Hover {
        contents: HoverContents::Markup(MarkupContent {
            kind: MarkupKind::Markdown,
            value: content,
        }),
        range: None,
    }
}

/// Format character hover information
fn format_character_hover(c: &crate::syntax::ast::Character) -> String {
    let mut content = format!("**character** `{}`\n\n", c.name);

    // Species
    if let Some(ref species) = c.species {
        content.push_str(&format!("**Species:** `{}`\n\n", species));
    }

    // Templates
    if let Some(ref templates) = c.template {
        content.push_str(&format!(
            "**Templates:** {}\n\n",
            templates
                .iter()
                .map(|t| format!("`{}`", t))
                .collect::<Vec<_>>()
                .join(", ")
        ));
    }

    // Fields
    if !c.fields.is_empty() {
        content.push_str("**Fields:**\n");
        for field in &c.fields {
            let value_preview = format_value_preview(&field.value);
            content.push_str(&format!("- `{}`: {}\n", field.name, value_preview));
        }
        content.push('\n');
    }

    // Prose blocks count
    let prose_count = c
        .fields
        .iter()
        .filter(|f| matches!(f.value, Value::ProseBlock(_)))
        .count();
    if prose_count > 0 {
        content.push_str(&format!("*{} prose block(s)*\n", prose_count));
    }

    content
}

/// Format template hover information
fn format_template_hover(t: &crate::syntax::ast::Template) -> String {
    let mut content = format!("**template** `{}`\n\n", t.name);

    if t.strict {
        content.push_str("*strict mode*\n\n");
    }

    // Includes
    if !t.includes.is_empty() {
        content.push_str(&format!(
            "**Includes:** {}\n\n",
            t.includes
                .iter()
                .map(|i| format!("`{}`", i))
                .collect::<Vec<_>>()
                .join(", ")
        ));
    }

    // Fields with types
    if !t.fields.is_empty() {
        content.push_str("**Fields:**\n");
        for field in &t.fields {
            let type_name = format_value_as_type(&field.value);
            content.push_str(&format!("- `{}`: {}\n", field.name, type_name));
        }
        content.push('\n');
    }

    content
}

/// Format species hover information
fn format_species_hover(s: &crate::syntax::ast::Species) -> String {
    let mut content = format!("**species** `{}`\n\n", s.name);

    // Includes
    if !s.includes.is_empty() {
        content.push_str(&format!(
            "**Includes:** {}\n\n",
            s.includes
                .iter()
                .map(|i| format!("`{}`", i))
                .collect::<Vec<_>>()
                .join(", ")
        ));
    }

    // Fields with types
    if !s.fields.is_empty() {
        content.push_str("**Fields:**\n");
        for field in &s.fields {
            let type_name = format_value_as_type(&field.value);
            content.push_str(&format!("- `{}`: {}\n", field.name, type_name));
        }
        content.push('\n');
    }

    content
}

/// Format location hover information
fn format_location_hover(l: &crate::syntax::ast::Location) -> String {
    let mut content = format!("**location** `{}`\n\n", l.name);

    if !l.fields.is_empty() {
        content.push_str("**Properties:**\n");
        for field in &l.fields {
            let value_preview = format_value_preview(&field.value);
            content.push_str(&format!("- `{}`: {}\n", field.name, value_preview));
        }
        content.push('\n');
    }

    content
}

/// Format institution hover information
fn format_institution_hover(i: &crate::syntax::ast::Institution) -> String {
    let mut content = format!("**institution** `{}`\n\n", i.name);

    if !i.fields.is_empty() {
        content.push_str("**Properties:**\n");
        for field in &i.fields {
            let value_preview = format_value_preview(&field.value);
            content.push_str(&format!("- `{}`: {}\n", field.name, value_preview));
        }
        content.push('\n');
    }

    content
}

/// Format relationship hover information
fn format_relationship_hover(r: &crate::syntax::ast::Relationship) -> String {
    let mut content = format!("**relationship** `{}`\n\n", r.name);

    // Participants
    if !r.participants.is_empty() {
        content.push_str(&format!(
            "**Participants:** {}\n\n",
            r.participants
                .iter()
                .map(|p| {
                    let name = p.name.join(".");
                    if let Some(ref role) = p.role {
                        format!("`{}` as {}", name, role)
                    } else {
                        format!("`{}`", name)
                    }
                })
                .collect::<Vec<_>>()
                .join(", ")
        ));
    }

    // Fields
    if !r.fields.is_empty() {
        content.push_str("**Fields:**\n");
        for field in &r.fields {
            let value_preview = format_value_preview(&field.value);
            content.push_str(&format!("- `{}`: {}\n", field.name, value_preview));
        }
        content.push('\n');
    }

    content
}

/// Format life arc hover information
fn format_life_arc_hover(la: &crate::syntax::ast::LifeArc) -> String {
    let mut content = format!("**life_arc** `{}`\n\n", la.name);

    if !la.states.is_empty() {
        content.push_str(&format!("**States:** {} states\n\n", la.states.len()));

        // Show first few states
        let preview_count = 5;
        for state in la.states.iter().take(preview_count) {
            content.push_str(&format!(
                "- `{}` ({} transitions)\n",
                state.name,
                state.transitions.len()
            ));
        }
        if la.states.len() > preview_count {
            content.push_str(&format!(
                "- *... and {} more*\n",
                la.states.len() - preview_count
            ));
        }
        content.push('\n');
    }

    content
}

/// Format schedule hover information
fn format_schedule_hover(s: &crate::syntax::ast::Schedule) -> String {
    let mut content = format!("**schedule** `{}`\n\n", s.name);

    if !s.blocks.is_empty() {
        content.push_str(&format!("**Time Blocks:** {} entries\n\n", s.blocks.len()));

        // Show first few blocks
        let preview_count = 5;
        for block in s.blocks.iter().take(preview_count) {
            let start_str = format_time(&block.start);
            let end_str = format_time(&block.end);
            content.push_str(&format!(
                "- {} - {}: {}\n",
                start_str, end_str, block.activity
            ));
        }
        if s.blocks.len() > preview_count {
            content.push_str(&format!(
                "- *... and {} more*\n",
                s.blocks.len() - preview_count
            ));
        }
        content.push('\n');
    }

    content
}

/// Format behavior hover information
fn format_behavior_hover(b: &crate::syntax::ast::Behavior) -> String {
    let mut content = format!("**behavior** `{}`\n\n", b.name);

    content.push_str("**Behavior Tree:**\n");
    content.push_str(&format_behavior_node_preview(&b.root, 0));
    content.push('\n');

    content
}

/// Format a behavior tree node preview (recursively, up to depth 2)
fn format_behavior_node_preview(node: &crate::syntax::ast::BehaviorNode, depth: usize) -> String {
    if depth > 2 {
        return format!("{}  *...*\n", "  ".repeat(depth));
    }

    let indent = "  ".repeat(depth);
    let mut content = String::new();

    match node {
        | crate::syntax::ast::BehaviorNode::Action(name, params) => {
            content.push_str(&format!("{}- Action: `{}`", indent, name));
            if !params.is_empty() {
                content.push_str(&format!(" ({} params)", params.len()));
            }
            content.push('\n');
        },
        | crate::syntax::ast::BehaviorNode::Sequence { children, .. } => {
            content.push_str(&format!(
                "{}- Sequence ({} children)\n",
                indent,
                children.len()
            ));
            for child in children.iter().take(3) {
                content.push_str(&format_behavior_node_preview(child, depth + 1));
            }
            if children.len() > 3 {
                content.push_str(&format!(
                    "{}  *... and {} more*\n",
                    indent,
                    children.len() - 3
                ));
            }
        },
        | crate::syntax::ast::BehaviorNode::Selector { children, .. } => {
            content.push_str(&format!(
                "{}- Selector ({} children)\n",
                indent,
                children.len()
            ));
            for child in children.iter().take(3) {
                content.push_str(&format_behavior_node_preview(child, depth + 1));
            }
            if children.len() > 3 {
                content.push_str(&format!(
                    "{}  *... and {} more*\n",
                    indent,
                    children.len() - 3
                ));
            }
        },
        | crate::syntax::ast::BehaviorNode::Condition(_) => {
            content.push_str(&format!("{}- Condition\n", indent));
        },
        | crate::syntax::ast::BehaviorNode::Decorator {
            decorator_type,
            child,
            ..
        } => {
            content.push_str(&format!("{}- Decorator: `{:?}`\n", indent, decorator_type));
            content.push_str(&format_behavior_node_preview(child, depth + 1));
        },
        | crate::syntax::ast::BehaviorNode::SubTree(name) => {
            content.push_str(&format!("{}- SubTree: `{}`\n", indent, name.join(".")));
        },
    }

    content
}

/// Format a value as a type name (for template/species fields)
fn format_value_as_type(value: &Value) -> String {
    match value {
        | Value::Identifier(path) => path.join("."),
        | Value::Text(_) => "Text".to_string(),
        | Value::Number(_) => "Number".to_string(),
        | Value::Decimal(_) => "Decimal".to_string(),
        | Value::Boolean(_) => "Boolean".to_string(),
        | Value::List(items) => {
            if items.is_empty() {
                "List".to_string()
            } else {
                format!("[{}]", format_value_as_type(&items[0]))
            }
        },
        | Value::Object(_) => "Object".to_string(),
        | Value::Range(start, end) => {
            format!(
                "{}..{}",
                format_value_as_type(start),
                format_value_as_type(end)
            )
        },
        | Value::Time(_) => "Time".to_string(),
        | Value::Duration(_) => "Duration".to_string(),
        | Value::ProseBlock(_) => "ProseBlock".to_string(),
        | Value::Override(_) => "Override".to_string(),
        | Value::Any => "Any".to_string(),
    }
}

/// Format a value preview (for character/location fields)
fn format_value_preview(value: &Value) -> String {
    match value {
        | Value::Identifier(path) => format!("`{}`", path.join(".")),
        | Value::Text(s) => format!("\"{}\"", truncate(s, 50)),
        | Value::Number(n) => n.to_string(),
        | Value::Decimal(f) => f.to_string(),
        | Value::Boolean(b) => b.to_string(),
        | Value::List(items) => {
            if items.is_empty() {
                "[]".to_string()
            } else {
                format!("[{} items]", items.len())
            }
        },
        | Value::Object(fields) => format!("{{{} fields}}", fields.len()),
        | Value::Range(start, end) => {
            format!(
                "{}..{}",
                format_value_preview(start),
                format_value_preview(end)
            )
        },
        | Value::Time(time) => format_time(time),
        | Value::Duration(duration) => format_duration(duration),
        | Value::ProseBlock(prose) => format!("*prose ({} chars)*", prose.content.len()),
        | Value::Override(override_val) => format!("*{} overrides*", override_val.overrides.len()),
        | Value::Any => "any".to_string(),
    }
}

/// Format a time value
fn format_time(time: &crate::syntax::ast::Time) -> String {
    if time.second == 0 {
        format!("{:02}:{:02}", time.hour, time.minute)
    } else {
        format!("{:02}:{:02}:{:02}", time.hour, time.minute, time.second)
    }
}

/// Format a duration value
fn format_duration(duration: &crate::syntax::ast::Duration) -> String {
    let mut parts = Vec::new();
    if duration.hours > 0 {
        parts.push(format!("{}h", duration.hours));
    }
    if duration.minutes > 0 {
        parts.push(format!("{}m", duration.minutes));
    }
    if duration.seconds > 0 {
        parts.push(format!("{}s", duration.seconds));
    }
    if parts.is_empty() {
        "0s".to_string()
    } else {
        parts.join(" ")
    }
}

/// Truncate a string to a maximum length
fn truncate(s: &str, max_len: usize) -> String {
    if s.len() <= max_len {
        s.to_string()
    } else {
        format!("{}...", &s[..max_len - 3])
    }
}
