//! Semantic analysis for LSP

use crate::frontend::ast::{Expr, ExprKind};
use crate::frontend::parser::Parser;
use std::collections::HashMap;
use tower_lsp::jsonrpc::Result;
use tower_lsp::lsp_types::{
    CompletionItem, CompletionItemKind, Diagnostic, DiagnosticSeverity, Documentation, Hover,
    HoverContents, Location, MarkedString, NumberOrString, Position, Range,
};

pub struct SemanticAnalyzer {
    symbol_table: SymbolTable,
}

#[derive(Debug, Default)]
struct SymbolTable {
    symbols: HashMap<String, Symbol>,
}

#[derive(Debug, Clone)]
struct Symbol {
    name: String,
    kind: String,
    documentation: Option<String>,
}

impl SemanticAnalyzer {
    /// Create a new semantic analyzer
    ///
    /// # Examples
    ///
    /// ```
    /// use ruchy::lsp::SemanticAnalyzer;
    ///
    /// let analyzer = SemanticAnalyzer::new();
    /// // Analyzer starts with empty symbol table
    /// ```
    pub fn new() -> Self {
        Self {
            symbol_table: SymbolTable::default(),
        }
    }

    /// Get completions for a position in the document
    ///
    /// # Errors
    ///
    /// This function currently does not return errors but returns Result for future compatibility
    pub fn get_completions(
        &self,
        _document: &str,
        _position: Position,
    ) -> Result<Vec<CompletionItem>> {
        // Basic keyword completions
        let keywords = vec![
            "fun", "let", "if", "else", "match", "struct", "trait", "impl", "actor", "import",
            "for", "while", "break", "continue", "true", "false",
        ];

        let mut completions = Vec::new();

        // Add keyword completions
        for keyword in keywords {
            completions.push(CompletionItem {
                label: keyword.to_string(),
                kind: Some(CompletionItemKind::KEYWORD),
                detail: Some(format!("Ruchy keyword: {keyword}")),
                ..Default::default()
            });
        }

        // Add built-in types
        let types = vec!["i32", "i64", "f32", "f64", "String", "bool", "()"];
        for type_name in types {
            completions.push(CompletionItem {
                label: type_name.to_string(),
                kind: Some(CompletionItemKind::TYPE_PARAMETER),
                detail: Some(format!("Built-in type: {type_name}")),
                ..Default::default()
            });
        }

        // Add symbols from symbol table
        for symbol in self.symbol_table.symbols.values() {
            completions.push(CompletionItem {
                label: symbol.name.clone(),
                kind: Some(CompletionItemKind::VARIABLE),
                detail: Some(symbol.kind.clone()),
                documentation: symbol
                    .documentation
                    .as_ref()
                    .map(|doc| Documentation::String(doc.clone())),
                ..Default::default()
            });
        }

        Ok(completions)
    }

    /// Get hover information for a position
    ///
    /// # Errors
    ///
    /// This function currently does not return errors but returns Result for future compatibility
    pub fn get_hover_info(&self, document: &str, _position: Position) -> Result<Option<Hover>> {
        // Parse the document to get context
        let mut parser = Parser::new(document);
        if parser.parse().is_err() {
            return Ok(None);
        }

        // For now, return basic info about Ruchy features
        let hover_text = "Ruchy Language\n\nA systems scripting language with:\n- Strong type inference\n- Actor-based concurrency\n- DataFrame-first collections\n- Rust interoperability";

        Ok(Some(Hover {
            contents: HoverContents::Scalar(MarkedString::String(hover_text.to_string())),
            range: None,
        }))
    }

    /// Get definition location for a symbol
    ///
    /// # Errors
    ///
    /// This function currently does not return errors but returns Result for future compatibility
    pub fn get_definition(&self, _document: &str, _position: Position) -> Result<Option<Location>> {
        // For now, return None - would need more sophisticated symbol tracking
        Ok(None)
    }

    /// Get diagnostics for a document
    ///
    /// # Errors
    ///
    /// This function currently does not return errors but returns Result for future compatibility
    pub fn get_diagnostics(&mut self, document: &str) -> Result<Vec<Diagnostic>> {
        let mut diagnostics = Vec::new();

        // Try to parse the document
        let mut parser = Parser::new(document);
        match parser.parse() {
            Ok(ast) => {
                // Update symbol table
                self.update_symbol_table(&ast, document);

                // Add any semantic warnings
                // For now, just check for unused variables (placeholder)
            }
            Err(parse_error) => {
                // Add parse error as diagnostic
                diagnostics.push(Diagnostic {
                    range: Range {
                        start: Position {
                            line: 0,
                            character: 0,
                        },
                        end: Position {
                            line: 0,
                            character: 10,
                        },
                    },
                    severity: Some(DiagnosticSeverity::ERROR),
                    code: Some(NumberOrString::String("parse_error".to_string())),
                    message: format!("Parse error: {parse_error}"),
                    source: Some("ruchy".to_string()),
                    ..Default::default()
                });
            }
        }

        Ok(diagnostics)
    }

    fn update_symbol_table(&mut self, ast: &Expr, document: &str) {
        // Walk the AST and extract symbols
        self.extract_symbols(ast, document);
    }

    fn extract_symbols(&mut self, expr: &Expr, _document: &str) {
        match &expr.kind {
            ExprKind::Function { name, params, .. } => {
                // Add function to symbol table
                let symbol = Symbol {
                    name: name.clone(),
                    kind: "function".to_string(),
                    documentation: None,
                };
                self.symbol_table.symbols.insert(name.clone(), symbol);

                // Add parameters
                for param in params {
                    let param_symbol = Symbol {
                        name: param.name(),
                        kind: "parameter".to_string(),
                        documentation: None,
                    };
                    self.symbol_table.symbols.insert(param.name(), param_symbol);
                }
            }
            ExprKind::Let { name, .. } => {
                let symbol = Symbol {
                    name: name.clone(),
                    kind: "variable".to_string(),
                    documentation: None,
                };
                self.symbol_table.symbols.insert(name.clone(), symbol);
            }
            ExprKind::Struct { name, .. } => {
                let symbol = Symbol {
                    name: name.clone(),
                    kind: "struct".to_string(),
                    documentation: None,
                };
                self.symbol_table.symbols.insert(name.clone(), symbol);
            }
            // Recursively process other expressions
            _ => {
                // Would recursively walk all child expressions
                // For now, just a placeholder
            }
        }
    }
}

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