//! LSP (Language Server Protocol) server for Lux
//!
//! Provides IDE features like:
//! - Diagnostics (errors and warnings)
//! - Hover information
//! - Go to definition
//! - Find references
//! - Completions
//! - Document symbols
//! - Rename refactoring
//! - Signature help
//! - Formatting

use crate::parser::Parser;
use crate::typechecker::TypeChecker;
use crate::symbol_table::{SymbolTable, SymbolKind};
use crate::formatter::{format as format_source, FormatConfig};

use lsp_server::{Connection, ExtractError, Message, Request, RequestId, Response};
use lsp_types::{
    notification::{DidChangeTextDocument, DidOpenTextDocument, Notification},
    request::{Completion, GotoDefinition, HoverRequest, References, DocumentSymbolRequest, Rename, SignatureHelpRequest, Formatting, InlayHintRequest},
    CompletionItem, CompletionItemKind, CompletionOptions, CompletionParams, CompletionResponse,
    Diagnostic, DiagnosticSeverity, DidChangeTextDocumentParams, DidOpenTextDocumentParams,
    GotoDefinitionParams, GotoDefinitionResponse, Hover, HoverContents, HoverParams,
    HoverProviderCapability, InitializeParams, MarkupContent, MarkupKind, Position,
    PublishDiagnosticsParams, Range, ServerCapabilities, TextDocumentSyncCapability,
    TextDocumentSyncKind, Url, ReferenceParams, Location, DocumentSymbolParams,
    DocumentSymbolResponse, SymbolInformation, RenameParams, WorkspaceEdit, TextEdit,
    SignatureHelpParams, SignatureHelp, SignatureInformation, ParameterInformation,
    SignatureHelpOptions, DocumentFormattingParams,
    InlayHint, InlayHintKind, InlayHintLabel, InlayHintParams,
};
use std::collections::HashMap;
use std::error::Error;

/// Cached document data
struct DocumentCache {
    text: String,
    symbol_table: Option<SymbolTable>,
}

/// LSP Server for Lux
pub struct LspServer {
    connection: Connection,
    /// Document contents and symbol tables by URI
    documents: HashMap<Url, DocumentCache>,
}

impl LspServer {
    /// Run the LSP server
    pub fn run() -> Result<(), Box<dyn Error + Sync + Send>> {
        eprintln!("Starting Lux LSP server...");

        // Create the transport connection
        let (connection, io_threads) = Connection::stdio();

        // Run the server
        let server = LspServer {
            connection,
            documents: HashMap::new(),
        };

        server.main_loop()?;

        // Wait for the I/O threads to finish
        io_threads.join()?;

        eprintln!("Lux LSP server stopped.");
        Ok(())
    }

    fn main_loop(mut self) -> Result<(), Box<dyn Error + Sync + Send>> {
        // Initialize
        let server_capabilities = serde_json::to_value(ServerCapabilities {
            text_document_sync: Some(TextDocumentSyncCapability::Kind(TextDocumentSyncKind::FULL)),
            hover_provider: Some(HoverProviderCapability::Simple(true)),
            completion_provider: Some(CompletionOptions {
                trigger_characters: Some(vec![".".to_string()]),
                ..Default::default()
            }),
            definition_provider: Some(lsp_types::OneOf::Left(true)),
            references_provider: Some(lsp_types::OneOf::Left(true)),
            document_symbol_provider: Some(lsp_types::OneOf::Left(true)),
            rename_provider: Some(lsp_types::OneOf::Left(true)),
            signature_help_provider: Some(SignatureHelpOptions {
                trigger_characters: Some(vec!["(".to_string(), ",".to_string()]),
                retrigger_characters: None,
                work_done_progress_options: Default::default(),
            }),
            document_formatting_provider: Some(lsp_types::OneOf::Left(true)),
            inlay_hint_provider: Some(lsp_types::OneOf::Left(true)),
            ..Default::default()
        })?;

        let init_params = self.connection.initialize(server_capabilities)?;
        let _init_params: InitializeParams = serde_json::from_value(init_params)?;

        eprintln!("LSP server initialized");

        // Main message loop
        loop {
            let msg = match self.connection.receiver.recv() {
                Ok(msg) => msg,
                Err(_) => break, // Channel closed
            };

            match msg {
                Message::Request(req) => {
                    if self.connection.handle_shutdown(&req)? {
                        return Ok(());
                    }
                    self.handle_request(req)?;
                }
                Message::Notification(not) => {
                    self.handle_notification(not)?;
                }
                Message::Response(_) => {}
            }
        }

        Ok(())
    }

    fn handle_request(&self, req: Request) -> Result<(), Box<dyn Error + Sync + Send>> {
        let req = match cast_request::<HoverRequest>(req) {
            Ok((id, params)) => {
                let result = self.handle_hover(params);
                let resp = Response::new_ok(id, result);
                self.connection.sender.send(Message::Response(resp))?;
                return Ok(());
            }
            Err(req) => req,
        };

        let req = match cast_request::<Completion>(req) {
            Ok((id, params)) => {
                let result = self.handle_completion(params);
                let resp = Response::new_ok(id, result);
                self.connection.sender.send(Message::Response(resp))?;
                return Ok(());
            }
            Err(req) => req,
        };

        let req = match cast_request::<GotoDefinition>(req) {
            Ok((id, params)) => {
                let result = self.handle_goto_definition(params);
                let resp = Response::new_ok(id, result);
                self.connection.sender.send(Message::Response(resp))?;
                return Ok(());
            }
            Err(req) => req,
        };

        let req = match cast_request::<References>(req) {
            Ok((id, params)) => {
                let result = self.handle_references(params);
                let resp = Response::new_ok(id, result);
                self.connection.sender.send(Message::Response(resp))?;
                return Ok(());
            }
            Err(req) => req,
        };

        let req = match cast_request::<DocumentSymbolRequest>(req) {
            Ok((id, params)) => {
                let result = self.handle_document_symbols(params);
                let resp = Response::new_ok(id, result);
                self.connection.sender.send(Message::Response(resp))?;
                return Ok(());
            }
            Err(req) => req,
        };

        let req = match cast_request::<Rename>(req) {
            Ok((id, params)) => {
                let result = self.handle_rename(params);
                let resp = Response::new_ok(id, result);
                self.connection.sender.send(Message::Response(resp))?;
                return Ok(());
            }
            Err(req) => req,
        };

        let req = match cast_request::<SignatureHelpRequest>(req) {
            Ok((id, params)) => {
                let result = self.handle_signature_help(params);
                let resp = Response::new_ok(id, result);
                self.connection.sender.send(Message::Response(resp))?;
                return Ok(());
            }
            Err(req) => req,
        };

        let req = match cast_request::<Formatting>(req) {
            Ok((id, params)) => {
                let result = self.handle_formatting(params);
                let resp = Response::new_ok(id, result);
                self.connection.sender.send(Message::Response(resp))?;
                return Ok(());
            }
            Err(req) => req,
        };

        let _req = match cast_request::<InlayHintRequest>(req) {
            Ok((id, params)) => {
                let result = self.handle_inlay_hints(params);
                let resp = Response::new_ok(id, result);
                self.connection.sender.send(Message::Response(resp))?;
                return Ok(());
            }
            Err(req) => req,
        };

        Ok(())
    }

    fn handle_notification(
        &mut self,
        not: lsp_server::Notification,
    ) -> Result<(), Box<dyn Error + Sync + Send>> {
        match not.method.as_str() {
            DidOpenTextDocument::METHOD => {
                let params: DidOpenTextDocumentParams = serde_json::from_value(not.params)?;
                let uri = params.text_document.uri;
                let text = params.text_document.text;
                self.update_document(uri.clone(), text.clone());
                self.publish_diagnostics(uri, &text)?;
            }
            DidChangeTextDocument::METHOD => {
                let params: DidChangeTextDocumentParams = serde_json::from_value(not.params)?;
                let uri = params.text_document.uri;
                if let Some(change) = params.content_changes.into_iter().last() {
                    let text = change.text.clone();
                    self.update_document(uri.clone(), text.clone());
                    self.publish_diagnostics(uri, &text)?;
                }
            }
            _ => {}
        }
        Ok(())
    }

    fn update_document(&mut self, uri: Url, text: String) {
        // Build symbol table if parsing succeeds
        let symbol_table = Parser::parse_source(&text)
            .ok()
            .map(|program| SymbolTable::build(&program));

        self.documents.insert(uri, DocumentCache {
            text,
            symbol_table,
        });
    }

    fn publish_diagnostics(
        &self,
        uri: Url,
        text: &str,
    ) -> Result<(), Box<dyn Error + Sync + Send>> {
        let diagnostics = self.get_diagnostics(text);

        let params = PublishDiagnosticsParams {
            uri,
            diagnostics,
            version: None,
        };

        self.connection.sender.send(Message::Notification(
            lsp_server::Notification::new(
                "textDocument/publishDiagnostics".to_string(),
                params,
            ),
        ))?;

        Ok(())
    }

    fn get_diagnostics(&self, source: &str) -> Vec<Diagnostic> {
        let mut diagnostics = Vec::new();

        // Parse the source
        let program = match Parser::parse_source(source) {
            Ok(prog) => prog,
            Err(err) => {
                diagnostics.push(Diagnostic {
                    range: span_to_range(source, err.span.start, err.span.end),
                    severity: Some(DiagnosticSeverity::ERROR),
                    message: err.message,
                    ..Default::default()
                });
                return diagnostics;
            }
        };

        // Type check
        let mut checker = TypeChecker::new();
        if let Err(errors) = checker.check_program(&program) {
            for error in errors {
                diagnostics.push(Diagnostic {
                    range: span_to_range(source, error.span.start, error.span.end),
                    severity: Some(DiagnosticSeverity::ERROR),
                    message: error.message,
                    ..Default::default()
                });
            }
        }

        diagnostics
    }

    fn handle_hover(&self, params: HoverParams) -> Option<Hover> {
        let uri = params.text_document_position_params.text_document.uri;
        let position = params.text_document_position_params.position;

        let doc = self.documents.get(&uri)?;
        let source = &doc.text;

        // Try to get info from symbol table first (position-based lookup)
        if let Some(ref table) = doc.symbol_table {
            let offset = self.position_to_offset(source, position);
            if let Some(symbol) = table.definition_at_position(offset) {
                return Some(self.format_symbol_hover(symbol));
            }
        }

        // Get the word under cursor
        let word = self.get_word_at_position(source, position)?;

        // When hovering on a keyword like 'fn', 'type', 'effect', 'let', 'trait',
        // look ahead to find the declaration name and show that symbol's info
        if let Some(ref table) = doc.symbol_table {
            if matches!(word.as_str(), "fn" | "type" | "effect" | "let" | "trait" | "handler" | "impl") {
                let offset = self.position_to_offset(source, position);
                if let Some(name) = self.find_next_ident(source, offset + word.len()) {
                    for sym in table.global_symbols() {
                        if sym.name == name {
                            return Some(self.format_symbol_hover(sym));
                        }
                    }
                }
            }

            // Try name-based lookup in symbol table (for usage sites)
            for sym in table.global_symbols() {
                if sym.name == word {
                    return Some(self.format_symbol_hover(sym));
                }
            }
        }

        // Check for module names (Console, List, String, etc.)
        if let Some(hover) = self.get_module_hover(&word) {
            return Some(hover);
        }

        // Rich documentation for behavioral property keywords
        if let Some((signature, doc_text)) = self.get_rich_symbol_info(&word) {
            return Some(Hover {
                contents: HoverContents::Markup(MarkupContent {
                    kind: MarkupKind::Markdown,
                    value: format!("```lux\n{}\n```\n\n{}", signature, doc_text),
                }),
                range: None,
            });
        }

        // Builtin keyword/function info
        if let Some((signature, doc_text)) = self.get_symbol_info(&word) {
            return Some(Hover {
                contents: HoverContents::Markup(MarkupContent {
                    kind: MarkupKind::Markdown,
                    value: format!("```lux\n{}\n```\n\n{}", signature, doc_text),
                }),
                range: None,
            });
        }

        None
    }

    /// Format a symbol into a hover response
    fn format_symbol_hover(&self, symbol: &crate::symbol_table::Symbol) -> Hover {
        let signature = symbol.type_signature.as_ref()
            .map(|s| s.as_str())
            .unwrap_or(&symbol.name);
        let kind_str = match symbol.kind {
            SymbolKind::Function => "function",
            SymbolKind::Variable => "variable",
            SymbolKind::Parameter => "parameter",
            SymbolKind::Type => "type",
            SymbolKind::TypeParameter => "type parameter",
            SymbolKind::Variant => "variant",
            SymbolKind::Effect => "effect",
            SymbolKind::EffectOperation => "effect operation",
            SymbolKind::Field => "field",
            SymbolKind::Module => "module",
        };
        let doc_str = symbol.documentation.as_ref()
            .map(|d| format!("\n\n{}", d))
            .unwrap_or_default();
        let formatted_sig = format_signature_for_hover(signature);
        let property_docs = extract_property_docs(signature);

        Hover {
            contents: HoverContents::Markup(MarkupContent {
                kind: MarkupKind::Markdown,
                value: format!(
                    "```lux\n{}\n```\n*{}*{}{}",
                    formatted_sig, kind_str, property_docs, doc_str
                ),
            }),
            range: None,
        }
    }

    /// Get hover info for built-in module names
    fn get_module_hover(&self, name: &str) -> Option<Hover> {
        let (sig, doc) = match name {
            "Console" => (
                "effect Console",
                "**Console I/O**\n\n\
                 - `Console.print(msg: String): Unit` — print to stdout\n\
                 - `Console.readLine(): String` — read a line from stdin\n\
                 - `Console.readInt(): Int` — read an integer from stdin",
            ),
            "File" => (
                "effect File",
                "**File System**\n\n\
                 - `File.read(path: String): String` — read file contents\n\
                 - `File.write(path: String, content: String): Unit` — write to file\n\
                 - `File.append(path: String, content: String): Unit` — append to file\n\
                 - `File.exists(path: String): Bool` — check if file exists\n\
                 - `File.delete(path: String): Unit` — delete a file\n\
                 - `File.list(path: String): List<String>` — list directory",
            ),
            "Http" => (
                "effect Http",
                "**HTTP Client**\n\n\
                 - `Http.get(url: String): String` — GET request\n\
                 - `Http.post(url: String, body: String): String` — POST request\n\
                 - `Http.put(url: String, body: String): String` — PUT request\n\
                 - `Http.delete(url: String): String` — DELETE request",
            ),
            "Sql" => (
                "effect Sql",
                "**SQL Database**\n\n\
                 - `Sql.open(path: String): Connection` — open database\n\
                 - `Sql.execute(conn: Connection, sql: String): Unit` — execute SQL\n\
                 - `Sql.query(conn: Connection, sql: String): List<Row>` — query rows\n\
                 - `Sql.close(conn: Connection): Unit` — close connection",
            ),
            "Random" => (
                "effect Random",
                "**Random Number Generation**\n\n\
                 - `Random.int(min: Int, max: Int): Int` — random integer\n\
                 - `Random.float(): Float` — random float 0.0–1.0\n\
                 - `Random.bool(): Bool` — random boolean",
            ),
            "Time" => (
                "effect Time",
                "**Time**\n\n\
                 - `Time.now(): Int` — current Unix timestamp (ms)\n\
                 - `Time.sleep(ms: Int): Unit` — sleep for milliseconds",
            ),
            "Process" => (
                "effect Process",
                "**Process / System**\n\n\
                 - `Process.exec(cmd: String): String` — run shell command\n\
                 - `Process.env(name: String): String` — get env variable\n\
                 - `Process.args(): List<String>` — command-line arguments\n\
                 - `Process.exit(code: Int): Unit` — exit with code",
            ),
            "Math" => (
                "module Math",
                "**Math Functions**\n\n\
                 - `Math.abs(n: Int): Int` — absolute value\n\
                 - `Math.min(a: Int, b: Int): Int` — minimum\n\
                 - `Math.max(a: Int, b: Int): Int` — maximum\n\
                 - `Math.sqrt(n: Float): Float` — square root\n\
                 - `Math.pow(base: Float, exp: Float): Float` — power\n\
                 - `Math.floor(n: Float): Int` — round down\n\
                 - `Math.ceil(n: Float): Int` — round up",
            ),
            "List" => (
                "module List",
                "**List Operations**\n\n\
                 - `List.map(list, f)` — transform each element\n\
                 - `List.filter(list, p)` — keep matching elements\n\
                 - `List.fold(list, init, f)` — reduce to single value\n\
                 - `List.head(list)` — first element (Option)\n\
                 - `List.tail(list)` — all except first (Option)\n\
                 - `List.length(list)` — number of elements\n\
                 - `List.concat(a, b)` — concatenate lists\n\
                 - `List.range(start, end)` — integer range\n\
                 - `List.reverse(list)` — reverse order\n\
                 - `List.get(list, i)` — element at index (Option)",
            ),
            "String" => (
                "module String",
                "**String Operations**\n\n\
                 - `String.length(s)` — string length\n\
                 - `String.split(s, delim)` — split by delimiter\n\
                 - `String.join(list, delim)` — join with delimiter\n\
                 - `String.trim(s)` — trim whitespace\n\
                 - `String.contains(s, sub)` — check substring\n\
                 - `String.replace(s, from, to)` — replace occurrences\n\
                 - `String.startsWith(s, prefix)` — check prefix\n\
                 - `String.endsWith(s, suffix)` — check suffix\n\
                 - `String.substring(s, start, end)` — extract range\n\
                 - `String.chars(s)` — list of characters",
            ),
            "Option" => (
                "type Option<A> = Some(A) | None",
                "**Optional Value**\n\n\
                 - `Option.isSome(opt)` — has a value?\n\
                 - `Option.isNone(opt)` — is empty?\n\
                 - `Option.getOrElse(opt, default)` — unwrap or default\n\
                 - `Option.map(opt, f)` — transform if present\n\
                 - `Option.flatMap(opt, f)` — chain operations",
            ),
            "Result" => (
                "type Result<A, E> = Ok(A) | Err(E)",
                "**Result of Fallible Operation**\n\n\
                 - `Result.isOk(r)` — succeeded?\n\
                 - `Result.isErr(r)` — failed?\n\
                 - `Result.map(r, f)` — transform success value\n\
                 - `Result.mapErr(r, f)` — transform error value",
            ),
            _ => return None,
        };

        Some(Hover {
            contents: HoverContents::Markup(MarkupContent {
                kind: MarkupKind::Markdown,
                value: format!("```lux\n{}\n```\n{}", sig, doc),
            }),
            range: None,
        })
    }

    fn get_word_at_position(&self, source: &str, position: Position) -> Option<String> {
        let lines: Vec<&str> = source.lines().collect();
        let line = lines.get(position.line as usize)?;
        let col = position.character as usize;

        // Find word boundaries
        let start = line[..col.min(line.len())]
            .rfind(|c: char| !c.is_alphanumeric() && c != '_')
            .map(|i| i + 1)
            .unwrap_or(0);

        let end = line[col.min(line.len())..]
            .find(|c: char| !c.is_alphanumeric() && c != '_')
            .map(|i| col + i)
            .unwrap_or(line.len());

        if start < end {
            Some(line[start..end].to_string())
        } else {
            None
        }
    }

    /// Find the next identifier in source after the given offset (skipping whitespace)
    fn find_next_ident(&self, source: &str, start: usize) -> Option<String> {
        let chars: Vec<char> = source.chars().collect();
        let mut pos = start;
        // Skip whitespace
        while pos < chars.len() && (chars[pos] == ' ' || chars[pos] == '\t' || chars[pos] == '\n' || chars[pos] == '\r') {
            pos += 1;
        }
        // Collect identifier
        let ident_start = pos;
        while pos < chars.len() && (chars[pos].is_alphanumeric() || chars[pos] == '_') {
            pos += 1;
        }
        if pos > ident_start {
            Some(chars[ident_start..pos].iter().collect())
        } else {
            None
        }
    }

    fn get_symbol_info(&self, word: &str) -> Option<(&'static str, &'static str)> {
        match word {
            // Keywords
            "fn" => Some(("fn name(params): ReturnType = body", "Declare a function")),
            "let" => Some(("let name = value", "Bind a value to a name")),
            "if" => Some(("if condition then expr else expr", "Conditional expression")),
            "match" => Some(("match value { pattern => expr, ... }", "Pattern matching")),
            "type" => Some(("type Name = TypeExpr", "Define a type alias or ADT")),
            "effect" => Some(("effect Name { fn op(): Type }", "Define an effect")),
            "handler" => Some(("handler { op() => impl }", "Handle effects")),
            "trait" => Some(("trait Name { fn method(): Type }", "Define a trait")),
            "impl" => Some(("impl Trait for Type { ... }", "Implement a trait")),

            // List functions
            "map" => Some(("List.map(list: List<A>, f: A -> B): List<B>", "Transform each element in a list")),
            "filter" => Some(("List.filter(list: List<A>, p: A -> Bool): List<A>", "Keep elements matching predicate")),
            "fold" => Some(("List.fold(list: List<A>, init: B, f: (B, A) -> B): B", "Reduce list to single value")),
            "reverse" => Some(("List.reverse(list: List<A>): List<A>", "Reverse a list")),
            "concat" => Some(("List.concat(a: List<A>, b: List<A>): List<A>", "Concatenate two lists")),
            "range" => Some(("List.range(start: Int, end: Int): List<Int>", "Create a list from start to end-1")),
            "length" => Some(("List.length(list: List<A>): Int", "Get the length of a list")),
            "head" => Some(("List.head(list: List<A>): Option<A>", "Get the first element")),
            "tail" => Some(("List.tail(list: List<A>): List<A>", "Get all elements except the first")),
            "isEmpty" => Some(("List.isEmpty(list: List<A>): Bool", "Check if list is empty")),

            // Option/Result
            "Some" => Some(("Some(value: A): Option<A>", "Wrap a value in Some")),
            "None" => Some(("None: Option<A>", "The empty Option")),
            "Ok" => Some(("Ok(value: A): Result<A, E>", "Successful result")),
            "Err" => Some(("Err(error: E): Result<A, E>", "Error result")),
            "isSome" => Some(("Option.isSome(opt: Option<A>): Bool", "Check if Option has a value")),
            "isNone" => Some(("Option.isNone(opt: Option<A>): Bool", "Check if Option is empty")),
            "getOrElse" => Some(("Option.getOrElse(opt: Option<A>, default: A): A", "Get value or default")),

            // Console
            "print" => Some(("Console.print(msg: String): Unit", "Print a message to the console")),
            "readLine" => Some(("Console.readLine(): String", "Read a line from input")),
            "readInt" => Some(("Console.readInt(): Int", "Read an integer from input")),

            // Types
            "Int" => Some(("type Int", "64-bit signed integer")),
            "Float" => Some(("type Float", "64-bit floating point number")),
            "Bool" => Some(("type Bool", "Boolean (true or false)")),
            "String" => Some(("type String", "UTF-8 string")),
            "Unit" => Some(("type Unit", "Unit type (no value)")),
            "List" => Some(("type List<A>", "Generic list/array type")),
            "Option" => Some(("type Option<A> = Some(A) | None", "Optional value (Some or None)")),
            "Result" => Some(("type Result<A, E> = Ok(A) | Err(E)", "Result of fallible operation")),

            // Built-in functions
            "toString" => Some(("toString(value: A): String", "Convert any value to a string")),

            _ => None,
        }
    }

    /// Rich documentation for behavioral properties and keywords
    fn get_rich_symbol_info(&self, word: &str) -> Option<(String, String)> {
        match word {
            "pure" => Some((
                "is pure".to_string(),
                "**Behavioral Property: Pure**\n\n\
                A pure function has no side effects and always produces the same output for the same inputs. \
                The compiler can safely memoize calls, reorder them, or eliminate duplicates.\n\n\
                ```lux\nfn add(a: Int, b: Int): Int is pure = a + b\n```\n\n\
                **Guarantees:**\n\
                - No effect operations (Console, File, Http, etc.)\n\
                - Referential transparency: `f(x)` can be replaced with its result\n\
                - Enables memoization and common subexpression elimination".to_string(),
            )),
            "total" => Some((
                "is total".to_string(),
                "**Behavioral Property: Total**\n\n\
                A total function always terminates and never throws exceptions. \
                The compiler verifies termination through structural recursion analysis.\n\n\
                ```lux\nfn factorial(n: Int): Int is total =\n    if n <= 0 then 1\n    else n * factorial(n - 1)\n```\n\n\
                **Guarantees:**\n\
                - Always produces a result (no infinite loops)\n\
                - Cannot use the `Fail` effect\n\
                - Recursive calls must be structurally decreasing".to_string(),
            )),
            "idempotent" => Some((
                "is idempotent".to_string(),
                "**Behavioral Property: Idempotent**\n\n\
                An idempotent function satisfies `f(f(x)) == f(x)` for all inputs. \
                Applying it multiple times has the same effect as applying it once.\n\n\
                ```lux\nfn abs(x: Int): Int is idempotent =\n    if x < 0 then 0 - x else x\n\n\
                fn clamp(x: Int): Int is idempotent =\n    if x < 0 then 0\n    else if x > 100 then 100\n    else x\n```\n\n\
                **Guarantees:**\n\
                - `f(f(x)) == f(x)` for all valid inputs\n\
                - Safe to retry without changing outcome\n\
                - Compiler can deduplicate consecutive calls".to_string(),
            )),
            "deterministic" => Some((
                "is deterministic".to_string(),
                "**Behavioral Property: Deterministic**\n\n\
                A deterministic function always produces the same output for the same inputs, \
                with no dependence on randomness, time, or external state.\n\n\
                ```lux\nfn multiply(a: Int, b: Int): Int is deterministic = a * b\n```\n\n\
                **Guarantees:**\n\
                - Cannot use `Random` or `Time` effects\n\
                - Same inputs always produce same outputs\n\
                - Results can be cached across runs".to_string(),
            )),
            "commutative" => Some((
                "is commutative".to_string(),
                "**Behavioral Property: Commutative**\n\n\
                A commutative function satisfies `f(a, b) == f(b, a)`. \
                The order of arguments doesn't affect the result.\n\n\
                ```lux\nfn add(a: Int, b: Int): Int is commutative = a + b\nfn max(a: Int, b: Int): Int is commutative =\n    if a > b then a else b\n```\n\n\
                **Guarantees:**\n\
                - Must have exactly 2 parameters\n\
                - `f(a, b) == f(b, a)` for all inputs\n\
                - Compiler can normalize argument order for optimization".to_string(),
            )),
            "run" => Some((
                "run expr with { handlers }".to_string(),
                "**Effect Handler**\n\n\
                Execute an effectful expression with explicit effect handlers. \
                Must be bound to a variable at top level.\n\n\
                ```lux\nlet result = run myFunction() with {\n    Console = { /* handler */ }\n}\n```\n\n\
                Handlers intercept effect operations and provide implementations.".to_string(),
            )),
            "with" => Some((
                "with {Effect1, Effect2}".to_string(),
                "**Effect Declaration / Handler Block**\n\n\
                Declares which effects a function may perform, or provides handlers in a `run` expression.\n\n\
                ```lux\n// In function signature:\nfn greet(name: String): Unit with {Console} =\n    Console.print(\"Hello, \" + name)\n\n\
                // In run expression:\nlet _ = run greet(\"world\") with {}\n```".to_string(),
            )),
            _ => None,
        }
    }

    fn handle_completion(&self, params: CompletionParams) -> Option<CompletionResponse> {
        let uri = params.text_document_position.text_document.uri;
        let position = params.text_document_position.position;

        // Check context to provide relevant completions
        let doc = self.documents.get(&uri)?;
        let source = &doc.text;
        let trigger_context = self.get_completion_context(source, position);

        let mut items = Vec::new();

        // If triggered after a dot, provide module-specific completions
        match trigger_context {
            CompletionContext::ModuleAccess(ref module) => {
                match module.as_str() {
                    "List" => items.extend(self.get_list_completions()),
                    "String" => items.extend(self.get_string_completions()),
                    "Option" | "Result" => items.extend(self.get_option_result_completions()),
                    "Console" => items.extend(self.get_console_completions()),
                    "Math" => items.extend(self.get_math_completions()),
                    "Sql" => items.extend(self.get_sql_completions()),
                    "File" => items.extend(self.get_file_completions()),
                    "Process" => items.extend(self.get_process_completions()),
                    "Http" => items.extend(self.get_http_completions()),
                    "Random" => items.extend(self.get_random_completions()),
                    "Time" => items.extend(self.get_time_completions()),
                    _ => {
                        // Unknown module, show all module completions
                        items.extend(self.get_list_completions());
                        items.extend(self.get_string_completions());
                        items.extend(self.get_option_result_completions());
                        items.extend(self.get_console_completions());
                        items.extend(self.get_math_completions());
                        items.extend(self.get_sql_completions());
                        items.extend(self.get_file_completions());
                        items.extend(self.get_process_completions());
                        items.extend(self.get_http_completions());
                        items.extend(self.get_random_completions());
                        items.extend(self.get_time_completions());
                    }
                }
            }
            CompletionContext::General => {
                // General completions (keywords + common functions)
                items.extend(self.get_keyword_completions());
                items.extend(self.get_builtin_completions());
                items.extend(self.get_type_completions());
            }
        }

        Some(CompletionResponse::Array(items))
    }

    fn get_completion_context(&self, source: &str, position: Position) -> CompletionContext {
        // Find the character before the cursor
        let offset = self.position_to_offset(source, position);
        if offset > 0 {
            let prev_char = source.chars().nth(offset - 1);
            if prev_char == Some('.') {
                // Extract the module name before the dot
                if let Some(module_name) = self.get_word_at_offset(source, offset.saturating_sub(2)) {
                    return CompletionContext::ModuleAccess(module_name);
                }
                return CompletionContext::ModuleAccess(String::new());
            }
        }
        CompletionContext::General
    }

    fn position_to_offset(&self, source: &str, position: Position) -> usize {
        let mut offset = 0;
        for (line_idx, line) in source.lines().enumerate() {
            if line_idx == position.line as usize {
                return offset + (position.character as usize).min(line.len());
            }
            offset += line.len() + 1; // +1 for newline
        }
        source.len()
    }

    fn get_keyword_completions(&self) -> Vec<CompletionItem> {
        vec![
            completion_item("fn", CompletionItemKind::KEYWORD, "Function declaration"),
            completion_item("let", CompletionItemKind::KEYWORD, "Variable binding"),
            completion_item("if", CompletionItemKind::KEYWORD, "Conditional expression"),
            completion_item("then", CompletionItemKind::KEYWORD, "Then branch"),
            completion_item("else", CompletionItemKind::KEYWORD, "Else branch"),
            completion_item("match", CompletionItemKind::KEYWORD, "Pattern matching"),
            completion_item("type", CompletionItemKind::KEYWORD, "Type declaration"),
            completion_item("effect", CompletionItemKind::KEYWORD, "Effect declaration"),
            completion_item("handler", CompletionItemKind::KEYWORD, "Effect handler"),
            completion_item("trait", CompletionItemKind::KEYWORD, "Trait declaration"),
            completion_item("impl", CompletionItemKind::KEYWORD, "Trait implementation"),
            completion_item("import", CompletionItemKind::KEYWORD, "Import declaration"),
            completion_item("return", CompletionItemKind::KEYWORD, "Return from function"),
            completion_item("run", CompletionItemKind::KEYWORD, "Run effectful computation"),
            completion_item("with", CompletionItemKind::KEYWORD, "With handler"),
            completion_item("true", CompletionItemKind::KEYWORD, "Boolean true"),
            completion_item("false", CompletionItemKind::KEYWORD, "Boolean false"),
        ]
    }

    fn get_builtin_completions(&self) -> Vec<CompletionItem> {
        vec![
            // Core modules
            completion_item("List", CompletionItemKind::MODULE, "List module"),
            completion_item("String", CompletionItemKind::MODULE, "String module"),
            completion_item("Console", CompletionItemKind::MODULE, "Console I/O effect"),
            completion_item("Math", CompletionItemKind::MODULE, "Math functions"),
            completion_item("Option", CompletionItemKind::MODULE, "Option type"),
            completion_item("Result", CompletionItemKind::MODULE, "Result type"),
            // Effect modules
            completion_item("Sql", CompletionItemKind::MODULE, "SQL database effect"),
            completion_item("File", CompletionItemKind::MODULE, "File system effect"),
            completion_item("Process", CompletionItemKind::MODULE, "Process/system effect"),
            completion_item("Http", CompletionItemKind::MODULE, "HTTP client effect"),
            completion_item("Random", CompletionItemKind::MODULE, "Random number effect"),
            completion_item("Time", CompletionItemKind::MODULE, "Time effect"),
            // Constructors
            completion_item("Some", CompletionItemKind::CONSTRUCTOR, "Option.Some constructor"),
            completion_item("None", CompletionItemKind::CONSTRUCTOR, "Option.None constructor"),
            completion_item("Ok", CompletionItemKind::CONSTRUCTOR, "Result.Ok constructor"),
            completion_item("Err", CompletionItemKind::CONSTRUCTOR, "Result.Err constructor"),
            // Functions
            completion_item("toString", CompletionItemKind::FUNCTION, "Convert value to string"),
        ]
    }

    fn get_type_completions(&self) -> Vec<CompletionItem> {
        vec![
            completion_item("Int", CompletionItemKind::TYPE_PARAMETER, "Integer type"),
            completion_item("Float", CompletionItemKind::TYPE_PARAMETER, "Floating point type"),
            completion_item("Bool", CompletionItemKind::TYPE_PARAMETER, "Boolean type"),
            completion_item("String", CompletionItemKind::TYPE_PARAMETER, "String type"),
            completion_item("Unit", CompletionItemKind::TYPE_PARAMETER, "Unit type"),
            completion_item("List", CompletionItemKind::TYPE_PARAMETER, "List type"),
            completion_item("Option", CompletionItemKind::TYPE_PARAMETER, "Option type"),
            completion_item("Result", CompletionItemKind::TYPE_PARAMETER, "Result type"),
        ]
    }

    fn get_list_completions(&self) -> Vec<CompletionItem> {
        vec![
            completion_item_with_doc("length", CompletionItemKind::METHOD, "List.length(list)", "Get the length of a list"),
            completion_item_with_doc("head", CompletionItemKind::METHOD, "List.head(list)", "Get first element (returns Option)"),
            completion_item_with_doc("tail", CompletionItemKind::METHOD, "List.tail(list)", "Get all but first element"),
            completion_item_with_doc("map", CompletionItemKind::METHOD, "List.map(list, fn)", "Transform each element"),
            completion_item_with_doc("filter", CompletionItemKind::METHOD, "List.filter(list, predicate)", "Keep elements matching predicate"),
            completion_item_with_doc("fold", CompletionItemKind::METHOD, "List.fold(list, init, fn)", "Reduce list to single value"),
            completion_item_with_doc("reverse", CompletionItemKind::METHOD, "List.reverse(list)", "Reverse list order"),
            completion_item_with_doc("concat", CompletionItemKind::METHOD, "List.concat(a, b)", "Concatenate two lists"),
            completion_item_with_doc("range", CompletionItemKind::METHOD, "List.range(start, end)", "Create list from range"),
            completion_item_with_doc("get", CompletionItemKind::METHOD, "List.get(list, index)", "Get element at index (returns Option)"),
            completion_item_with_doc("find", CompletionItemKind::METHOD, "List.find(list, predicate)", "Find first matching element"),
            completion_item_with_doc("isEmpty", CompletionItemKind::METHOD, "List.isEmpty(list)", "Check if list is empty"),
            completion_item_with_doc("take", CompletionItemKind::METHOD, "List.take(list, n)", "Take first n elements"),
            completion_item_with_doc("drop", CompletionItemKind::METHOD, "List.drop(list, n)", "Drop first n elements"),
            completion_item_with_doc("any", CompletionItemKind::METHOD, "List.any(list, predicate)", "Check if any element matches"),
            completion_item_with_doc("all", CompletionItemKind::METHOD, "List.all(list, predicate)", "Check if all elements match"),
        ]
    }

    fn get_string_completions(&self) -> Vec<CompletionItem> {
        vec![
            completion_item_with_doc("length", CompletionItemKind::METHOD, "String.length(s)", "Get string length"),
            completion_item_with_doc("split", CompletionItemKind::METHOD, "String.split(s, delimiter)", "Split string by delimiter"),
            completion_item_with_doc("join", CompletionItemKind::METHOD, "String.join(list, delimiter)", "Join strings with delimiter"),
            completion_item_with_doc("trim", CompletionItemKind::METHOD, "String.trim(s)", "Remove leading/trailing whitespace"),
            completion_item_with_doc("contains", CompletionItemKind::METHOD, "String.contains(s, substr)", "Check if contains substring"),
            completion_item_with_doc("replace", CompletionItemKind::METHOD, "String.replace(s, from, to)", "Replace occurrences"),
            completion_item_with_doc("chars", CompletionItemKind::METHOD, "String.chars(s)", "Get list of characters"),
            completion_item_with_doc("lines", CompletionItemKind::METHOD, "String.lines(s)", "Split into lines"),
            completion_item_with_doc("fromChar", CompletionItemKind::METHOD, "String.fromChar(c)", "Convert char to string"),
        ]
    }

    fn get_option_result_completions(&self) -> Vec<CompletionItem> {
        vec![
            completion_item_with_doc("isSome", CompletionItemKind::METHOD, "Option.isSome(opt)", "Check if Option has value"),
            completion_item_with_doc("isNone", CompletionItemKind::METHOD, "Option.isNone(opt)", "Check if Option is empty"),
            completion_item_with_doc("getOrElse", CompletionItemKind::METHOD, "Option.getOrElse(opt, default)", "Get value or default"),
            completion_item_with_doc("map", CompletionItemKind::METHOD, "Option.map(opt, fn)", "Transform value if present"),
            completion_item_with_doc("flatMap", CompletionItemKind::METHOD, "Option.flatMap(opt, fn)", "Chain Option operations"),
            completion_item_with_doc("isOk", CompletionItemKind::METHOD, "Result.isOk(result)", "Check if Result is Ok"),
            completion_item_with_doc("isErr", CompletionItemKind::METHOD, "Result.isErr(result)", "Check if Result is Err"),
        ]
    }

    fn get_console_completions(&self) -> Vec<CompletionItem> {
        vec![
            completion_item_with_doc("print", CompletionItemKind::METHOD, "Console.print(msg)", "Print to console"),
            completion_item_with_doc("readLine", CompletionItemKind::METHOD, "Console.readLine()", "Read line from input"),
            completion_item_with_doc("readInt", CompletionItemKind::METHOD, "Console.readInt()", "Read integer from input"),
        ]
    }

    fn get_math_completions(&self) -> Vec<CompletionItem> {
        vec![
            completion_item_with_doc("abs", CompletionItemKind::METHOD, "Math.abs(n)", "Absolute value"),
            completion_item_with_doc("min", CompletionItemKind::METHOD, "Math.min(a, b)", "Minimum of two values"),
            completion_item_with_doc("max", CompletionItemKind::METHOD, "Math.max(a, b)", "Maximum of two values"),
            completion_item_with_doc("sqrt", CompletionItemKind::METHOD, "Math.sqrt(n)", "Square root"),
            completion_item_with_doc("pow", CompletionItemKind::METHOD, "Math.pow(base, exp)", "Power function"),
            completion_item_with_doc("floor", CompletionItemKind::METHOD, "Math.floor(n)", "Round down"),
            completion_item_with_doc("ceil", CompletionItemKind::METHOD, "Math.ceil(n)", "Round up"),
            completion_item_with_doc("round", CompletionItemKind::METHOD, "Math.round(n)", "Round to nearest"),
        ]
    }

    fn get_sql_completions(&self) -> Vec<CompletionItem> {
        vec![
            completion_item_with_doc("open", CompletionItemKind::METHOD, "Sql.open(path)", "Open SQLite database file"),
            completion_item_with_doc("openMemory", CompletionItemKind::METHOD, "Sql.openMemory()", "Open in-memory database"),
            completion_item_with_doc("close", CompletionItemKind::METHOD, "Sql.close(conn)", "Close database connection"),
            completion_item_with_doc("execute", CompletionItemKind::METHOD, "Sql.execute(conn, sql)", "Execute SQL statement"),
            completion_item_with_doc("query", CompletionItemKind::METHOD, "Sql.query(conn, sql)", "Query and return rows"),
            completion_item_with_doc("queryOne", CompletionItemKind::METHOD, "Sql.queryOne(conn, sql)", "Query single row"),
            completion_item_with_doc("beginTx", CompletionItemKind::METHOD, "Sql.beginTx(conn)", "Begin transaction"),
            completion_item_with_doc("commit", CompletionItemKind::METHOD, "Sql.commit(conn)", "Commit transaction"),
            completion_item_with_doc("rollback", CompletionItemKind::METHOD, "Sql.rollback(conn)", "Rollback transaction"),
        ]
    }

    fn get_file_completions(&self) -> Vec<CompletionItem> {
        vec![
            completion_item_with_doc("read", CompletionItemKind::METHOD, "File.read(path)", "Read file contents"),
            completion_item_with_doc("write", CompletionItemKind::METHOD, "File.write(path, content)", "Write to file"),
            completion_item_with_doc("append", CompletionItemKind::METHOD, "File.append(path, content)", "Append to file"),
            completion_item_with_doc("exists", CompletionItemKind::METHOD, "File.exists(path)", "Check if file exists"),
            completion_item_with_doc("delete", CompletionItemKind::METHOD, "File.delete(path)", "Delete file"),
            completion_item_with_doc("list", CompletionItemKind::METHOD, "File.list(path)", "List directory contents"),
        ]
    }

    fn get_process_completions(&self) -> Vec<CompletionItem> {
        vec![
            completion_item_with_doc("exec", CompletionItemKind::METHOD, "Process.exec(cmd)", "Execute shell command"),
            completion_item_with_doc("env", CompletionItemKind::METHOD, "Process.env(name)", "Get environment variable"),
            completion_item_with_doc("args", CompletionItemKind::METHOD, "Process.args()", "Get command-line arguments"),
            completion_item_with_doc("cwd", CompletionItemKind::METHOD, "Process.cwd()", "Get current directory"),
            completion_item_with_doc("exit", CompletionItemKind::METHOD, "Process.exit(code)", "Exit with code"),
        ]
    }

    fn get_http_completions(&self) -> Vec<CompletionItem> {
        vec![
            completion_item_with_doc("get", CompletionItemKind::METHOD, "Http.get(url)", "HTTP GET request"),
            completion_item_with_doc("post", CompletionItemKind::METHOD, "Http.post(url, body)", "HTTP POST request"),
            completion_item_with_doc("put", CompletionItemKind::METHOD, "Http.put(url, body)", "HTTP PUT request"),
            completion_item_with_doc("delete", CompletionItemKind::METHOD, "Http.delete(url)", "HTTP DELETE request"),
        ]
    }

    fn get_random_completions(&self) -> Vec<CompletionItem> {
        vec![
            completion_item_with_doc("int", CompletionItemKind::METHOD, "Random.int(min, max)", "Random integer in range"),
            completion_item_with_doc("float", CompletionItemKind::METHOD, "Random.float()", "Random float 0.0-1.0"),
            completion_item_with_doc("bool", CompletionItemKind::METHOD, "Random.bool()", "Random boolean"),
        ]
    }

    fn get_time_completions(&self) -> Vec<CompletionItem> {
        vec![
            completion_item_with_doc("now", CompletionItemKind::METHOD, "Time.now()", "Current Unix timestamp (ms)"),
            completion_item_with_doc("sleep", CompletionItemKind::METHOD, "Time.sleep(ms)", "Sleep for milliseconds"),
        ]
    }

    fn handle_goto_definition(
        &self,
        params: GotoDefinitionParams,
    ) -> Option<GotoDefinitionResponse> {
        let uri = params.text_document_position_params.text_document.uri;
        let position = params.text_document_position_params.position;
        let doc = self.documents.get(&uri)?;
        let source = &doc.text;

        // Try symbol table first
        if let Some(ref table) = doc.symbol_table {
            let offset = self.position_to_offset(source, position);
            if let Some(symbol) = table.definition_at_position(offset) {
                let range = span_to_range(source, symbol.span.start, symbol.span.end);
                return Some(GotoDefinitionResponse::Scalar(Location {
                    uri,
                    range,
                }));
            }
        }

        // Fall back to pattern matching
        let offset = self.position_to_offset(source, position);
        let word = self.get_word_at_offset(source, offset)?;

        // Search for function definition in the same file
        // Look for "fn <word>" pattern
        let fn_pattern = format!("fn {}", word);
        if let Some(def_offset) = source.find(&fn_pattern) {
            let range = span_to_range(source, def_offset + 3, def_offset + 3 + word.len());
            return Some(GotoDefinitionResponse::Scalar(Location {
                uri,
                range,
            }));
        }

        // Look for "let <word>" pattern
        let let_pattern = format!("let {} ", word);
        if let Some(def_offset) = source.find(&let_pattern) {
            let range = span_to_range(source, def_offset + 4, def_offset + 4 + word.len());
            return Some(GotoDefinitionResponse::Scalar(Location {
                uri,
                range,
            }));
        }

        // Look for type definition "type <word>"
        let type_pattern = format!("type {}", word);
        if let Some(def_offset) = source.find(&type_pattern) {
            let range = span_to_range(source, def_offset + 5, def_offset + 5 + word.len());
            return Some(GotoDefinitionResponse::Scalar(Location {
                uri,
                range,
            }));
        }

        None
    }

    fn handle_references(&self, params: ReferenceParams) -> Option<Vec<Location>> {
        let uri = params.text_document_position.text_document.uri;
        let position = params.text_document_position.position;
        let doc = self.documents.get(&uri)?;
        let source = &doc.text;

        if let Some(ref table) = doc.symbol_table {
            let offset = self.position_to_offset(source, position);
            if let Some(symbol) = table.definition_at_position(offset) {
                let refs = table.find_references(symbol.id);
                let locations: Vec<Location> = refs.iter()
                    .map(|r| Location {
                        uri: uri.clone(),
                        range: span_to_range(source, r.span.start, r.span.end),
                    })
                    .collect();
                return Some(locations);
            }
        }

        None
    }

    fn handle_document_symbols(&self, params: DocumentSymbolParams) -> Option<DocumentSymbolResponse> {
        let uri = params.text_document.uri;
        let doc = self.documents.get(&uri)?;
        let source = &doc.text;

        if let Some(ref table) = doc.symbol_table {
            let symbols: Vec<SymbolInformation> = table.global_symbols()
                .iter()
                .map(|sym| {
                    #[allow(deprecated)]
                    SymbolInformation {
                        name: sym.name.clone(),
                        kind: symbol_kind_to_lsp(&sym.kind),
                        tags: None,
                        deprecated: None,
                        location: Location {
                            uri: uri.clone(),
                            range: span_to_range(source, sym.span.start, sym.span.end),
                        },
                        container_name: None,
                    }
                })
                .collect();
            return Some(DocumentSymbolResponse::Flat(symbols));
        }

        None
    }

    fn get_word_at_offset(&self, source: &str, offset: usize) -> Option<String> {
        let chars: Vec<char> = source.chars().collect();
        if offset >= chars.len() {
            return None;
        }

        // Find start of word
        let mut start = offset;
        while start > 0 && (chars[start - 1].is_alphanumeric() || chars[start - 1] == '_') {
            start -= 1;
        }

        // Find end of word
        let mut end = offset;
        while end < chars.len() && (chars[end].is_alphanumeric() || chars[end] == '_') {
            end += 1;
        }

        if start == end {
            return None;
        }

        Some(chars[start..end].iter().collect())
    }

    fn handle_rename(&self, params: RenameParams) -> Option<WorkspaceEdit> {
        let uri = params.text_document_position.text_document.uri;
        let position = params.text_document_position.position;
        let new_name = params.new_name;
        let doc = self.documents.get(&uri)?;
        let source = &doc.text;

        if let Some(ref table) = doc.symbol_table {
            let offset = self.position_to_offset(source, position);
            if let Some(symbol) = table.definition_at_position(offset) {
                // Find all references to this symbol
                let refs = table.find_references(symbol.id);

                // Create text edits for each reference
                let edits: Vec<TextEdit> = refs.iter()
                    .map(|r| TextEdit {
                        range: span_to_range(source, r.span.start, r.span.end),
                        new_text: new_name.clone(),
                    })
                    .collect();

                // Return workspace edit
                let mut changes = HashMap::new();
                changes.insert(uri, edits);

                return Some(WorkspaceEdit {
                    changes: Some(changes),
                    document_changes: None,
                    change_annotations: None,
                });
            }
        }

        None
    }

    fn handle_signature_help(&self, params: SignatureHelpParams) -> Option<SignatureHelp> {
        let uri = params.text_document_position_params.text_document.uri;
        let position = params.text_document_position_params.position;
        let doc = self.documents.get(&uri)?;
        let source = &doc.text;

        let offset = self.position_to_offset(source, position);

        // Find the function call context by searching backwards for '('
        let chars: Vec<char> = source.chars().collect();
        let mut paren_depth = 0;
        let mut comma_count = 0;
        let mut func_start = offset;

        for i in (0..offset).rev() {
            let c = chars.get(i)?;
            match c {
                ')' => paren_depth += 1,
                '(' => {
                    if paren_depth == 0 {
                        func_start = i;
                        break;
                    }
                    paren_depth -= 1;
                }
                ',' if paren_depth == 0 => comma_count += 1,
                _ => {}
            }
        }

        // Get the function name before the opening paren
        if func_start == 0 {
            return None;
        }

        let func_name = self.get_word_at_offset(source, func_start - 1)?;

        // Look up function in symbol table
        if let Some(ref table) = doc.symbol_table {
            // Search for function definition
            for sym in table.global_symbols() {
                if sym.name == func_name {
                    if let Some(ref sig) = sym.type_signature {
                        // Parse parameters from signature
                        let params = self.extract_parameters_from_signature(sig);

                        let signature_info = SignatureInformation {
                            label: sig.clone(),
                            documentation: sym.documentation.as_ref().map(|d| {
                                lsp_types::Documentation::MarkupContent(MarkupContent {
                                    kind: MarkupKind::Markdown,
                                    value: d.clone(),
                                })
                            }),
                            parameters: Some(params),
                            active_parameter: Some(comma_count as u32),
                        };

                        return Some(SignatureHelp {
                            signatures: vec![signature_info],
                            active_signature: Some(0),
                            active_parameter: Some(comma_count as u32),
                        });
                    }
                }
            }
        }

        // Fall back to hardcoded signatures for built-in functions
        self.get_builtin_signature(&func_name, comma_count)
    }

    fn extract_parameters_from_signature(&self, sig: &str) -> Vec<ParameterInformation> {
        // Parse "fn name(a: Int, b: String): ReturnType" format
        let mut params = Vec::new();

        if let Some(start) = sig.find('(') {
            if let Some(end) = sig.find(')') {
                let params_str = &sig[start + 1..end];
                for param in params_str.split(',') {
                    let param = param.trim();
                    if !param.is_empty() {
                        params.push(ParameterInformation {
                            label: lsp_types::ParameterLabel::Simple(param.to_string()),
                            documentation: None,
                        });
                    }
                }
            }
        }

        params
    }

    fn get_builtin_signature(&self, func_name: &str, active_param: usize) -> Option<SignatureHelp> {
        let (sig, params): (&str, Vec<&str>) = match func_name {
            // List functions
            "map" => ("fn map<A, B>(list: List<A>, f: fn(A): B): List<B>", vec!["list: List<A>", "f: fn(A): B"]),
            "filter" => ("fn filter<A>(list: List<A>, f: fn(A): Bool): List<A>", vec!["list: List<A>", "f: fn(A): Bool"]),
            "fold" => ("fn fold<A, B>(list: List<A>, init: B, f: fn(B, A): B): B", vec!["list: List<A>", "init: B", "f: fn(B, A): B"]),
            "head" => ("fn head<A>(list: List<A>): Option<A>", vec!["list: List<A>"]),
            "tail" => ("fn tail<A>(list: List<A>): Option<List<A>>", vec!["list: List<A>"]),
            "concat" => ("fn concat<A>(a: List<A>, b: List<A>): List<A>", vec!["a: List<A>", "b: List<A>"]),
            "length" => ("fn length<A>(list: List<A>): Int", vec!["list: List<A>"]),
            "get" => ("fn get<A>(list: List<A>, index: Int): Option<A>", vec!["list: List<A>", "index: Int"]),
            // String functions
            "split" => ("fn split(s: String, sep: String): List<String>", vec!["s: String", "sep: String"]),
            "join" => ("fn join(list: List<String>, sep: String): String", vec!["list: List<String>", "sep: String"]),
            "replace" => ("fn replace(s: String, from: String, to: String): String", vec!["s: String", "from: String", "to: String"]),
            "substring" => ("fn substring(s: String, start: Int, end: Int): String", vec!["s: String", "start: Int", "end: Int"]),
            "contains" => ("fn contains(s: String, sub: String): Bool", vec!["s: String", "sub: String"]),
            // Option functions
            "getOrElse" => ("fn getOrElse<A>(opt: Option<A>, default: A): A", vec!["opt: Option<A>", "default: A"]),
            // Result functions
            "mapErr" => ("fn mapErr<E, E2, T>(result: Result<T, E>, f: fn(E): E2): Result<T, E2>", vec!["result: Result<T, E>", "f: fn(E): E2"]),
            _ => return None,
        };

        let param_infos: Vec<ParameterInformation> = params.iter()
            .map(|p| ParameterInformation {
                label: lsp_types::ParameterLabel::Simple(p.to_string()),
                documentation: None,
            })
            .collect();

        Some(SignatureHelp {
            signatures: vec![SignatureInformation {
                label: sig.to_string(),
                documentation: None,
                parameters: Some(param_infos),
                active_parameter: Some(active_param as u32),
            }],
            active_signature: Some(0),
            active_parameter: Some(active_param as u32),
        })
    }

    fn handle_inlay_hints(&self, params: InlayHintParams) -> Option<Vec<InlayHint>> {
        let uri = params.text_document.uri;
        let doc = self.documents.get(&uri)?;
        let source = &doc.text;

        // Parse the document to get AST
        let program = Parser::parse_source(source).ok()?;

        // Type-check to get inferred types
        let mut checker = TypeChecker::new();
        let _ = checker.check_program(&program);

        let mut hints = Vec::new();

        // Collect parameter names for known functions (from symbol table)
        let param_names = self.collect_function_params(&program);

        for decl in &program.declarations {
            match decl {
                crate::ast::Declaration::Let(l) => {
                    // Show inferred type for let bindings without explicit type annotations
                    if l.typ.is_none() {
                        if let Some(inferred_type) = checker.get_inferred_type(&l.name.name) {
                            let type_str = format!(": {}", inferred_type);
                            let pos = offset_to_position(source, l.name.span.end);
                            hints.push(InlayHint {
                                position: pos,
                                label: InlayHintLabel::String(type_str),
                                kind: Some(InlayHintKind::TYPE),
                                text_edits: None,
                                tooltip: None,
                                padding_left: Some(false),
                                padding_right: Some(true),
                                data: None,
                            });
                        }
                    }
                    // Walk into the value expression for call-site parameter hints
                    collect_call_site_hints(source, &l.value, &param_names, &mut hints);
                }
                crate::ast::Declaration::Function(f) => {
                    // Walk into the function body for call-site parameter hints
                    collect_call_site_hints(source, &f.body, &param_names, &mut hints);
                }
                _ => {}
            }
        }

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

    /// Collect parameter names for all functions defined in the program
    fn collect_function_params(&self, program: &crate::ast::Program) -> HashMap<String, Vec<String>> {
        let mut params = HashMap::new();
        for decl in &program.declarations {
            if let crate::ast::Declaration::Function(f) = decl {
                let names: Vec<String> = f.params.iter()
                    .map(|p| p.name.name.clone())
                    .collect();
                params.insert(f.name.name.clone(), names);
            }
        }
        // Add builtin function parameter names
        params.insert("map".into(), vec!["list".into(), "f".into()]);
        params.insert("filter".into(), vec!["list".into(), "predicate".into()]);
        params.insert("fold".into(), vec!["list".into(), "init".into(), "f".into()]);
        params.insert("concat".into(), vec!["a".into(), "b".into()]);
        params.insert("range".into(), vec!["start".into(), "end".into()]);
        params.insert("get".into(), vec!["list".into(), "index".into()]);
        params.insert("take".into(), vec!["list".into(), "n".into()]);
        params.insert("drop".into(), vec!["list".into(), "n".into()]);
        params.insert("split".into(), vec!["s".into(), "delimiter".into()]);
        params.insert("join".into(), vec!["list".into(), "delimiter".into()]);
        params.insert("replace".into(), vec!["s".into(), "old".into(), "new".into()]);
        params.insert("substring".into(), vec!["s".into(), "start".into(), "end".into()]);
        params.insert("contains".into(), vec!["s".into(), "substr".into()]);
        params.insert("getOrElse".into(), vec!["opt".into(), "default".into()]);
        params
    }

    fn handle_formatting(&self, params: DocumentFormattingParams) -> Option<Vec<TextEdit>> {
        let uri = params.text_document.uri;
        let doc = self.documents.get(&uri)?;
        let source = &doc.text;

        // Use the Lux formatter with default config
        let config = FormatConfig::default();
        match format_source(source, &config) {
            Ok(formatted) => {
                if formatted == *source {
                    // No changes needed
                    return Some(vec![]);
                }

                // Replace entire document
                let lines: Vec<&str> = source.lines().collect();
                let last_line = lines.len().saturating_sub(1);
                let last_col = lines.last().map(|l| l.len()).unwrap_or(0);

                Some(vec![TextEdit {
                    range: Range {
                        start: Position { line: 0, character: 0 },
                        end: Position {
                            line: last_line as u32,
                            character: last_col as u32,
                        },
                    },
                    new_text: formatted,
                }])
            }
            Err(_) => {
                // Formatting failed, return no edits
                None
            }
        }
    }
}

/// Convert byte offsets to LSP Position
/// Format a function signature for hover display, wrapping long lines
fn format_signature_for_hover(sig: &str) -> String {
    // If it fits in ~60 chars, keep it on one line
    if sig.len() <= 60 {
        return sig.to_string();
    }

    // Try to break at parameter list for function signatures
    if let Some(paren_start) = sig.find('(') {
        if let Some(paren_end) = sig.rfind(')') {
            let prefix = &sig[..paren_start + 1];
            let params = &sig[paren_start + 1..paren_end];
            let suffix = &sig[paren_end..];

            // Split parameters and format each on its own line
            let param_parts: Vec<&str> = params.split(", ").collect();
            if param_parts.len() > 1 {
                let indent = "    ";
                let formatted_params = param_parts.join(&format!(",\n{}", indent));
                return format!("{}\n{}{}\n{}", prefix, indent, formatted_params, suffix);
            }
        }
    }

    sig.to_string()
}

/// Extract behavioral property documentation from a signature string
fn extract_property_docs(sig: &str) -> String {
    let properties = [
        ("is pure", "**pure** — no side effects, same output for same inputs"),
        ("is total", "**total** — always terminates, no exceptions"),
        ("is idempotent", "**idempotent** — `f(f(x)) == f(x)`"),
        ("is deterministic", "**deterministic** — no randomness or time dependence"),
        ("is commutative", "**commutative** — `f(a, b) == f(b, a)`"),
    ];

    let mut found = Vec::new();
    for (keyword, description) in &properties {
        if sig.contains(keyword) {
            found.push(*description);
        }
    }

    if found.is_empty() {
        String::new()
    } else {
        format!("\n\n{}", found.join("  \n"))
    }
}

/// Recursively collect parameter name hints at call sites
fn collect_call_site_hints(
    source: &str,
    expr: &crate::ast::Expr,
    param_names: &HashMap<String, Vec<String>>,
    hints: &mut Vec<InlayHint>,
) {
    use crate::ast::Expr;
    match expr {
        Expr::Call { func, args, .. } => {
            // Get the function name for parameter lookup
            let func_name = match func.as_ref() {
                Expr::Var(ident) => Some(ident.name.clone()),
                // Module.method calls like List.map
                Expr::Field { object, field, .. } => {
                    if let Expr::Var(_) = object.as_ref() {
                        Some(field.name.clone())
                    } else {
                        None
                    }
                }
                _ => None,
            };

            if let Some(name) = func_name {
                if let Some(names) = param_names.get(&name) {
                    for (i, arg) in args.iter().enumerate() {
                        if let Some(param_name) = names.get(i) {
                            // Skip hint if the argument is already a variable with the same name
                            if let Expr::Var(ident) = arg {
                                if &ident.name == param_name {
                                    continue;
                                }
                            }
                            // Skip hints for single-arg functions (obvious)
                            if args.len() <= 1 {
                                continue;
                            }
                            let pos = offset_to_position(source, arg.span().start);
                            hints.push(InlayHint {
                                position: pos,
                                label: InlayHintLabel::String(format!("{}:", param_name)),
                                kind: Some(InlayHintKind::PARAMETER),
                                text_edits: None,
                                tooltip: None,
                                padding_left: Some(false),
                                padding_right: Some(true),
                                data: None,
                            });
                        }
                    }
                }
            }

            // Recurse into function expression and arguments
            collect_call_site_hints(source, func, param_names, hints);
            for arg in args {
                collect_call_site_hints(source, arg, param_names, hints);
            }
        }
        Expr::BinaryOp { left, right, .. } => {
            collect_call_site_hints(source, left, param_names, hints);
            collect_call_site_hints(source, right, param_names, hints);
        }
        Expr::UnaryOp { operand, .. } => {
            collect_call_site_hints(source, operand, param_names, hints);
        }
        Expr::If { condition, then_branch, else_branch, .. } => {
            collect_call_site_hints(source, condition, param_names, hints);
            collect_call_site_hints(source, then_branch, param_names, hints);
            collect_call_site_hints(source, else_branch, param_names, hints);
        }
        Expr::Let { value, body, .. } => {
            collect_call_site_hints(source, value, param_names, hints);
            collect_call_site_hints(source, body, param_names, hints);
        }
        Expr::Block { statements, result, .. } => {
            for stmt in statements {
                match stmt {
                    crate::ast::Statement::Expr(e) => {
                        collect_call_site_hints(source, e, param_names, hints);
                    }
                    crate::ast::Statement::Let { value, .. } => {
                        collect_call_site_hints(source, value, param_names, hints);
                    }
                }
            }
            collect_call_site_hints(source, result, param_names, hints);
        }
        Expr::Match { scrutinee, arms, .. } => {
            collect_call_site_hints(source, scrutinee, param_names, hints);
            for arm in arms {
                collect_call_site_hints(source, &arm.body, param_names, hints);
            }
        }
        Expr::Lambda { body, .. } => {
            collect_call_site_hints(source, body, param_names, hints);
        }
        Expr::Tuple { elements, .. } | Expr::List { elements, .. } => {
            for e in elements {
                collect_call_site_hints(source, e, param_names, hints);
            }
        }
        Expr::Record { fields, .. } => {
            for (_, e) in fields {
                collect_call_site_hints(source, e, param_names, hints);
            }
        }
        Expr::Field { object, .. } => {
            collect_call_site_hints(source, object, param_names, hints);
        }
        Expr::Run { expr, handlers, .. } => {
            collect_call_site_hints(source, expr, param_names, hints);
            for (_, handler_expr) in handlers {
                collect_call_site_hints(source, handler_expr, param_names, hints);
            }
        }
        Expr::Resume { value, .. } => {
            collect_call_site_hints(source, value, param_names, hints);
        }
        Expr::EffectOp { args, .. } => {
            for arg in args {
                collect_call_site_hints(source, arg, param_names, hints);
            }
        }
        Expr::Literal { .. } | Expr::Var(_) => {}
    }
}

fn span_to_range(source: &str, start: usize, end: usize) -> Range {
    let start_pos = offset_to_position(source, start);
    let end_pos = offset_to_position(source, end);
    Range {
        start: start_pos,
        end: end_pos,
    }
}

fn offset_to_position(source: &str, offset: usize) -> Position {
    let mut line = 0u32;
    let mut col = 0u32;

    for (i, c) in source.char_indices() {
        if i >= offset {
            break;
        }
        if c == '\n' {
            line += 1;
            col = 0;
        } else {
            col += 1;
        }
    }

    Position {
        line,
        character: col,
    }
}

fn cast_request<R>(req: Request) -> Result<(RequestId, R::Params), Request>
where
    R: lsp_types::request::Request,
    R::Params: serde::de::DeserializeOwned,
{
    match req.extract(R::METHOD) {
        Ok(params) => Ok(params),
        Err(ExtractError::MethodMismatch(req)) => Err(req),
        Err(ExtractError::JsonError { .. }) => {
            // This shouldn't happen if the client is well-behaved
            panic!("JSON deserialization error in LSP request")
        }
    }
}

/// Context for completion suggestions
#[derive(PartialEq)]
enum CompletionContext {
    /// After a dot with specific module (e.g., "List.", "Sql.")
    ModuleAccess(String),
    /// General context (keywords, types, etc.)
    General,
}

/// Create a simple completion item
fn completion_item(label: &str, kind: CompletionItemKind, detail: &str) -> CompletionItem {
    CompletionItem {
        label: label.to_string(),
        kind: Some(kind),
        detail: Some(detail.to_string()),
        ..Default::default()
    }
}

/// Create a completion item with documentation
fn completion_item_with_doc(
    label: &str,
    kind: CompletionItemKind,
    signature: &str,
    doc: &str,
) -> CompletionItem {
    CompletionItem {
        label: label.to_string(),
        kind: Some(kind),
        detail: Some(signature.to_string()),
        documentation: Some(lsp_types::Documentation::MarkupContent(MarkupContent {
            kind: MarkupKind::Markdown,
            value: doc.to_string(),
        })),
        ..Default::default()
    }
}

/// Convert symbol kind to LSP symbol kind
fn symbol_kind_to_lsp(kind: &SymbolKind) -> lsp_types::SymbolKind {
    match kind {
        SymbolKind::Function => lsp_types::SymbolKind::FUNCTION,
        SymbolKind::Variable => lsp_types::SymbolKind::VARIABLE,
        SymbolKind::Parameter => lsp_types::SymbolKind::VARIABLE,
        SymbolKind::Type => lsp_types::SymbolKind::CLASS,
        SymbolKind::TypeParameter => lsp_types::SymbolKind::TYPE_PARAMETER,
        SymbolKind::Variant => lsp_types::SymbolKind::ENUM_MEMBER,
        SymbolKind::Effect => lsp_types::SymbolKind::INTERFACE,
        SymbolKind::EffectOperation => lsp_types::SymbolKind::METHOD,
        SymbolKind::Field => lsp_types::SymbolKind::FIELD,
        SymbolKind::Module => lsp_types::SymbolKind::MODULE,
    }
}
