//! Inlay hints for implicit information
//!
//! Provides inline annotations showing:
//! - Parameter names in action calls
//! - Inferred types for field values
//! - Template/species field sources

use tower_lsp::lsp_types::{
    InlayHint,
    InlayHintKind,
    InlayHintLabel,
    Position,
};

use super::document::Document;
use crate::syntax::ast::{
    Declaration,
    Field,
    Value,
};

/// Get inlay hints for a document range
pub fn get_inlay_hints(doc: &Document, start: Position, end: Position) -> Option<Vec<InlayHint>> {
    let ast = doc.ast.as_ref()?;
    let mut hints = Vec::new();
    let mut positions = doc.positions.clone();

    // Convert positions to offsets
    let start_line = start.line as usize;
    let end_line = end.line as usize;

    // Process all declarations
    for decl in &ast.declarations {
        match decl {
            | Declaration::Character(character) => {
                // Skip if character is outside requested range
                if character.span.start_line > end_line || character.span.end_line < start_line {
                    continue;
                }

                // Add type hints for character fields
                for field in &character.fields {
                    if field.span.start_line >= start_line && field.span.start_line <= end_line {
                        add_type_hint(&mut hints, &mut positions, field);
                    }
                }
            },
            | Declaration::Template(template) => {
                // Skip if template is outside requested range
                if template.span.start_line > end_line || template.span.end_line < start_line {
                    continue;
                }

                // Add type hints for template fields
                for field in &template.fields {
                    if field.span.start_line >= start_line && field.span.start_line <= end_line {
                        add_type_hint(&mut hints, &mut positions, field);
                    }
                }
            },
            | Declaration::Species(species) => {
                // Skip if species is outside requested range
                if species.span.start_line > end_line || species.span.end_line < start_line {
                    continue;
                }

                // Add type hints for species fields
                for field in &species.fields {
                    if field.span.start_line >= start_line && field.span.start_line <= end_line {
                        add_type_hint(&mut hints, &mut positions, field);
                    }
                }
            },
            | Declaration::Institution(institution) => {
                // Skip if institution is outside requested range
                if institution.span.start_line > end_line || institution.span.end_line < start_line
                {
                    continue;
                }

                // Add type hints for institution fields
                for field in &institution.fields {
                    if field.span.start_line >= start_line && field.span.start_line <= end_line {
                        add_type_hint(&mut hints, &mut positions, field);
                    }
                }
            },
            | Declaration::Location(location) => {
                // Skip if location is outside requested range
                if location.span.start_line > end_line || location.span.end_line < start_line {
                    continue;
                }

                // Add type hints for location fields
                for field in &location.fields {
                    if field.span.start_line >= start_line && field.span.start_line <= end_line {
                        add_type_hint(&mut hints, &mut positions, field);
                    }
                }
            },
            | Declaration::Relationship(relationship) => {
                // Skip if relationship is outside requested range
                if relationship.span.start_line > end_line ||
                    relationship.span.end_line < start_line
                {
                    continue;
                }

                // Add type hints for relationship fields
                for field in &relationship.fields {
                    if field.span.start_line >= start_line && field.span.start_line <= end_line {
                        add_type_hint(&mut hints, &mut positions, field);
                    }
                }
            },
            | Declaration::Behavior(behavior) => {
                // Skip if behavior is outside requested range
                if behavior.span.start_line > end_line || behavior.span.end_line < start_line {
                    continue;
                }

                // TODO: Add parameter name hints for action calls in behavior
                // trees Would need to traverse BehaviorNode
                // tree and match actions to schema
            },
            | _ => {},
        }
    }

    if hints.is_empty() {
        None
    } else {
        Some(hints)
    }
}

/// Add type hint for a field value
fn add_type_hint(
    hints: &mut Vec<InlayHint>,
    positions: &mut crate::position::PositionTracker,
    field: &Field,
) {
    let type_str = infer_value_type(&field.value);

    // Only add hints for non-obvious types
    // Skip if the type is clear from the literal (e.g., "string", 123, true)
    let should_hint = match &field.value {
        | Value::Text(_) | Value::Number(_) | Value::Decimal(_) | Value::Boolean(_) => false,
        | Value::Identifier(_) => true, // Show type for identifier references
        | Value::List(_) => true,       // Show list element type
        | Value::Object(_) => false,    // Object structure is visible
        | Value::Range(_, _) => false,  // Range syntax is clear
        | Value::Time(_) => false,      // Time format is clear
        | Value::Duration(_) => false,  // Duration format is clear
        | Value::ProseBlock(_) => false, // Prose is obvious
        | Value::Override(_) => true,   // Show what's being overridden
        | Value::Any => true,           // Show Any type marker
    };

    if !should_hint {
        return;
    }

    // Position the hint at the end of the field value
    let (line, col) = positions.offset_to_position(field.span.end);

    hints.push(InlayHint {
        position: Position {
            line: line as u32,
            character: col as u32,
        },
        label: InlayHintLabel::String(format!(": {}", type_str)),
        kind: Some(InlayHintKind::TYPE),
        text_edits: None,
        tooltip: None,
        padding_left: Some(true),
        padding_right: None,
        data: None,
    });
}

/// Infer the type of a value for display
fn infer_value_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() {
                "[]".to_string()
            } else {
                format!("[{}]", infer_value_type(&items[0]))
            }
        },
        | Value::Object(_) => "Object".to_string(),
        | Value::Range(start, end) => {
            format!("{}..{}", infer_value_type(start), infer_value_type(end))
        },
        | Value::Time(_) => "Time".to_string(),
        | Value::Duration(_) => "Duration".to_string(),
        | Value::ProseBlock(_) => "Prose".to_string(),
        | Value::Override(_) => "Override".to_string(),
        | Value::Any => "Any".to_string(),
    }
}
