//! Parser for el-ui component files.
//!
//! Hand-written recursive descent. Produces a `Vec<Component>` from a token stream.

use crate::ast::*;
use crate::error::{CompileError, CompileResult};
use crate::lexer::Token;

pub fn parse(tokens: &[Token]) -> CompileResult<Vec<Component>> {
    let mut p = Parser::new(tokens);
    p.parse_program()
}

struct Parser<'a> {
    tokens: &'a [Token],
    pos: usize,
}

impl<'a> Parser<'a> {
    fn new(tokens: &'a [Token]) -> Self {
        Self { tokens, pos: 0 }
    }

    fn peek(&self) -> &Token {
        self.tokens.get(self.pos).unwrap_or(&Token::Eof)
    }

    fn advance(&mut self) -> &Token {
        let tok = self.tokens.get(self.pos).unwrap_or(&Token::Eof);
        if self.pos < self.tokens.len() {
            self.pos += 1;
        }
        tok
    }

    fn expect(&mut self, expected: &Token) -> CompileResult<()> {
        let tok = self.advance();
        if tok == expected {
            Ok(())
        } else {
            Err(CompileError::Parse {
                msg: format!("expected {:?}, got {:?}", expected, tok),
            })
        }
    }

    fn expect_ident(&mut self) -> CompileResult<String> {
        match self.advance().clone() {
            Token::Ident(s) => Ok(s),
            other => Err(CompileError::Parse {
                msg: format!("expected identifier, got {:?}", other),
            }),
        }
    }

    fn parse_program(&mut self) -> CompileResult<Vec<Component>> {
        let mut components = Vec::new();
        while *self.peek() != Token::Eof {
            match self.peek() {
                Token::Component => {
                    components.push(self.parse_component()?);
                }
                _ => {
                    // Skip unknown top-level tokens
                    self.advance();
                }
            }
        }
        Ok(components)
    }

    fn parse_component(&mut self) -> CompileResult<Component> {
        self.expect(&Token::Component)?;
        let name = self.expect_ident()?;
        self.expect(&Token::LBrace)?;

        let mut props = Vec::new();
        let mut state = Vec::new();
        let mut methods = Vec::new();
        let mut template = Template { nodes: Vec::new() };

        loop {
            match self.peek().clone() {
                Token::RBrace => {
                    self.advance();
                    break;
                }
                Token::Eof => break,
                Token::Props => {
                    self.advance();
                    props = self.parse_prop_block()?;
                }
                Token::State => {
                    self.advance();
                    state = self.parse_state_block()?;
                }
                Token::Fn => {
                    methods.push(self.parse_method()?);
                }
                Token::Template => {
                    self.advance(); // consume `template`
                    template = self.parse_template_block()?;
                }
                _ => {
                    self.advance(); // skip unknown
                }
            }
        }

        Ok(Component { name, props, state, methods, template })
    }

    fn parse_prop_block(&mut self) -> CompileResult<Vec<PropDef>> {
        self.expect(&Token::LBrace)?;
        let mut props = Vec::new();
        loop {
            match self.peek().clone() {
                Token::RBrace | Token::Eof => {
                    self.advance();
                    break;
                }
                Token::Ident(name) => {
                    self.advance();
                    self.expect(&Token::Colon)?;
                    let type_name = self.parse_type_name()?;
                    let default = if *self.peek() == Token::Eq {
                        self.advance();
                        Some(self.parse_default_value()?)
                    } else {
                        None
                    };
                    // Optional trailing comma/semicolon
                    if matches!(self.peek(), Token::Comma | Token::Semicolon) {
                        self.advance();
                    }
                    props.push(PropDef { name, type_name, default });
                }
                _ => {
                    self.advance();
                }
            }
        }
        Ok(props)
    }

    fn parse_state_block(&mut self) -> CompileResult<Vec<StateDef>> {
        self.expect(&Token::LBrace)?;
        let mut defs = Vec::new();
        loop {
            match self.peek().clone() {
                Token::RBrace | Token::Eof => {
                    self.advance();
                    break;
                }
                Token::Ident(name) => {
                    self.advance();
                    self.expect(&Token::Colon)?;
                    let type_name = self.parse_type_name()?;
                    self.expect(&Token::Eq)?;
                    let initial = self.parse_default_value()?;
                    if matches!(self.peek(), Token::Comma | Token::Semicolon) {
                        self.advance();
                    }
                    defs.push(StateDef { name, type_name, initial });
                }
                _ => {
                    self.advance();
                }
            }
        }
        Ok(defs)
    }

    fn parse_type_name(&mut self) -> CompileResult<String> {
        let name = match self.peek().clone() {
            Token::Ident(s) => { self.advance(); s }
            other => return Err(CompileError::Parse {
                msg: format!("expected type name, got {:?}", other),
            }),
        };
        // Check for array type [T] → already consumed base name, but array types
        // start with [ so this handles bare type names only
        Ok(name)
    }

    fn parse_default_value(&mut self) -> CompileResult<String> {
        // Collect tokens until comma, semicolon, or next top-level item
        // We need to handle nested structures like Fn types, etc.
        let mut result = String::new();
        let mut depth = 0i32;
        loop {
            match self.peek() {
                Token::Eof => break,
                Token::LParen | Token::LBrace | Token::LBracket => {
                    depth += 1;
                    let tok = self.advance();
                    result.push_str(&token_to_str(tok));
                }
                Token::RParen | Token::RBrace | Token::RBracket => {
                    if depth == 0 { break; }
                    depth -= 1;
                    let tok = self.advance();
                    result.push_str(&token_to_str(tok));
                }
                Token::Comma | Token::Semicolon if depth == 0 => break,
                // These signal end of the value if at depth 0
                Token::Ident(_) | Token::Props | Token::State | Token::Fn
                | Token::Template | Token::Component if depth == 0 => break,
                _ => {
                    let tok = self.advance();
                    result.push_str(&token_to_str(tok));
                    result.push(' ');
                }
            }
        }
        Ok(result.trim().to_owned())
    }

    fn parse_method(&mut self) -> CompileResult<Method> {
        self.expect(&Token::Fn)?;
        let name = self.expect_ident()?;
        self.expect(&Token::LParen)?;

        let mut params: Vec<(String, String)> = Vec::new();
        loop {
            match self.peek().clone() {
                Token::RParen | Token::Eof => { self.advance(); break; }
                Token::Ident(pname) => {
                    self.advance();
                    self.expect(&Token::Colon)?;
                    let ptype = self.parse_type_name()?;
                    params.push((pname, ptype));
                    if *self.peek() == Token::Comma { self.advance(); }
                }
                _ => { self.advance(); }
            }
        }

        self.expect(&Token::Arrow)?;
        let return_type = self.parse_type_name()?;

        // Read the method body between { }
        let body = self.read_block_raw()?;

        Ok(Method { name, params, return_type, body })
    }

    /// Read everything between { and matching } as raw text.
    fn read_block_raw(&mut self) -> CompileResult<String> {
        self.expect(&Token::LBrace)?;
        let mut result = String::new();
        let mut depth = 1i32;
        loop {
            match self.peek() {
                Token::Eof => break,
                Token::LBrace => { depth += 1; self.advance(); result.push_str("{ "); }
                Token::RBrace => {
                    depth -= 1;
                    self.advance();
                    if depth == 0 { break; }
                    result.push_str("} ");
                }
                tok => {
                    result.push_str(&token_to_str(tok));
                    result.push(' ');
                    self.advance();
                }
            }
        }
        Ok(result.trim().to_owned())
    }

    /// Parse the template block. At this point the lexer has already expanded
    /// the template into special tokens (LBrace/RBrace wrapping interpolations,
    /// LAngle/Ident for elements, etc.).
    fn parse_template_block(&mut self) -> CompileResult<Template> {
        self.expect(&Token::LBrace)?;
        let nodes = self.parse_template_nodes()?;
        // The matching RBrace is consumed inside parse_template_nodes
        Ok(Template { nodes })
    }

    fn parse_template_nodes(&mut self) -> CompileResult<Vec<TemplateNode>> {
        let mut nodes: Vec<TemplateNode> = Vec::new();
        loop {
            match self.peek().clone() {
                Token::Eof | Token::RBrace => {
                    self.advance();
                    break;
                }
                Token::CloseTag(_) => {
                    // Consumed by parent element parser
                    break;
                }
                Token::LAngle => {
                    nodes.push(self.parse_element()?);
                }
                Token::LBrace => {
                    // Interpolation: { RawText }
                    self.advance(); // consume {
                    if let Token::RawText(expr) = self.peek().clone() {
                        self.advance();
                        nodes.push(TemplateNode::Interpolation(expr));
                        if *self.peek() == Token::RBrace { self.advance(); }
                    } else {
                        nodes.push(TemplateNode::Text("{".into()));
                    }
                }
                Token::HashIdent(kw) => {
                    let kw = kw.clone();
                    self.advance();
                    nodes.push(self.parse_block_tag(&kw)?);
                }
                Token::SlashIdent(_) => {
                    // End of a block — caller handles
                    break;
                }
                Token::ColonIdent(_) => {
                    // {:else} — caller handles
                    break;
                }
                Token::RawText(t) => {
                    let t = t.clone();
                    self.advance();
                    if !t.is_empty() {
                        nodes.push(TemplateNode::Text(t));
                    }
                }
                _ => {
                    self.advance(); // skip
                }
            }
        }
        Ok(nodes)
    }

    fn parse_element(&mut self) -> CompileResult<TemplateNode> {
        self.expect(&Token::LAngle)?;
        let tag = self.expect_ident()?;

        let is_component = tag.chars().next().map(|c| c.is_uppercase()).unwrap_or(false);

        let mut attrs: Vec<Attr> = Vec::new();

        // Parse attributes until > or />
        let mut self_closing = false;
        loop {
            match self.peek().clone() {
                Token::SelfClose => {
                    self.advance();
                    self_closing = true;
                    break;
                }
                Token::RAngle => {
                    self.advance();
                    break;
                }
                Token::Eof => break,
                Token::OnColon(event) => {
                    let event = event.clone();
                    self.advance();
                    let handler = if let Token::RawText(h) = self.peek().clone() {
                        self.advance();
                        h
                    } else {
                        String::new()
                    };
                    attrs.push(Attr::EventHandler { event, handler });
                }
                Token::Ident(name) => {
                    let name = name.clone();
                    self.advance();
                    if *self.peek() == Token::Eq {
                        self.advance(); // consume =
                        match self.peek().clone() {
                            Token::StringLit(val) => {
                                self.advance();
                                attrs.push(Attr::Static { name, value: val });
                            }
                            Token::RawText(expr) => {
                                self.advance();
                                // Determine if it's a bool attr
                                // Simple heuristic: if name is "disabled", "checked", "readonly"
                                let bool_attrs = ["disabled", "checked", "readonly", "required", "multiple", "selected"];
                                if bool_attrs.contains(&name.as_str()) {
                                    attrs.push(Attr::BoolAttr { name, expr });
                                } else {
                                    attrs.push(Attr::Dynamic { name, expr });
                                }
                            }
                            _ => {
                                attrs.push(Attr::Static { name, value: String::new() });
                            }
                        }
                    } else {
                        // Standalone attribute (boolean)
                        attrs.push(Attr::BoolAttr { name, expr: "true".into() });
                    }
                }
                _ => {
                    self.advance();
                }
            }
        }

        if self_closing || is_component {
            if is_component {
                return Ok(TemplateNode::Component { name: tag, props: attrs });
            }
            return Ok(TemplateNode::Element { tag, attrs, children: Vec::new() });
        }

        // Read children until </tag>
        let children = self.parse_template_children(&tag)?;

        Ok(TemplateNode::Element { tag, attrs, children })
    }

    fn parse_template_children(&mut self, close_tag: &str) -> CompileResult<Vec<TemplateNode>> {
        let mut children: Vec<TemplateNode> = Vec::new();
        loop {
            match self.peek().clone() {
                Token::Eof => break,
                Token::RBrace => break,
                Token::CloseTag(tag) => {
                    self.advance();
                    if tag == close_tag || tag.is_empty() {
                        break;
                    }
                    // Mismatched close tag — ignore
                }
                Token::LAngle => {
                    children.push(self.parse_element()?);
                }
                Token::LBrace => {
                    self.advance();
                    if let Token::RawText(expr) = self.peek().clone() {
                        self.advance();
                        children.push(TemplateNode::Interpolation(expr));
                        if *self.peek() == Token::RBrace { self.advance(); }
                    }
                }
                Token::HashIdent(kw) => {
                    let kw = kw.clone();
                    self.advance();
                    children.push(self.parse_block_tag(&kw)?);
                }
                Token::SlashIdent(_) | Token::ColonIdent(_) => break,
                Token::RawText(t) => {
                    let t = t.clone();
                    self.advance();
                    if !t.trim().is_empty() {
                        children.push(TemplateNode::Text(t));
                    }
                }
                _ => { self.advance(); }
            }
        }
        Ok(children)
    }

    fn parse_block_tag(&mut self, kw: &str) -> CompileResult<TemplateNode> {
        match kw {
            "if" => self.parse_if_block(),
            "each" => self.parse_each_block(),
            "activate" => self.parse_activate_block(),
            _ => Err(CompileError::Parse { msg: format!("unknown block tag: #{}", kw) }),
        }
    }

    fn parse_if_block(&mut self) -> CompileResult<TemplateNode> {
        // Next token should be RawText with the condition
        let condition = if let Token::RawText(cond) = self.peek().clone() {
            self.advance();
            cond
        } else {
            return Err(CompileError::Parse { msg: "expected condition after {#if}".into() });
        };

        let then = self.parse_template_nodes_until_close_or_else()?;

        let else_ = if let Token::ColonIdent(kw) = self.peek().clone() {
            if kw == "else" {
                self.advance();
                Some(self.parse_template_nodes_until_close_or_else()?)
            } else {
                None
            }
        } else {
            None
        };

        // Consume {/if}
        if let Token::SlashIdent(kw) = self.peek().clone() {
            if kw == "if" { self.advance(); }
        }

        Ok(TemplateNode::If { condition, then, else_ })
    }

    fn parse_each_block(&mut self) -> CompileResult<TemplateNode> {
        // RawText: "items as item"
        let raw = if let Token::RawText(r) = self.peek().clone() {
            self.advance();
            r
        } else {
            return Err(CompileError::Parse { msg: "expected 'items as item' after {#each}".into() });
        };

        let (items, item_name) = parse_each_header(&raw)?;
        let children = self.parse_template_nodes_until_close_or_else()?;

        if let Token::SlashIdent(kw) = self.peek().clone() {
            if kw == "each" { self.advance(); }
        }

        Ok(TemplateNode::Each { items, item_name, children })
    }

    fn parse_activate_block(&mut self) -> CompileResult<TemplateNode> {
        // RawText: `"query" as results`
        let raw = if let Token::RawText(r) = self.peek().clone() {
            self.advance();
            r
        } else {
            return Err(CompileError::Parse { msg: "expected query after {#activate}".into() });
        };

        let (query, result_name) = parse_activate_header(&raw)?;
        let children = self.parse_template_nodes_until_close_or_else()?;

        if let Token::SlashIdent(kw) = self.peek().clone() {
            if kw == "activate" { self.advance(); }
        }

        Ok(TemplateNode::Activate { query, result_name, children })
    }

    fn parse_template_nodes_until_close_or_else(&mut self) -> CompileResult<Vec<TemplateNode>> {
        let mut nodes: Vec<TemplateNode> = Vec::new();
        loop {
            match self.peek().clone() {
                Token::Eof | Token::RBrace => break,
                Token::SlashIdent(_) | Token::ColonIdent(_) => break,
                Token::CloseTag(_) => break,
                Token::LAngle => nodes.push(self.parse_element()?),
                Token::LBrace => {
                    self.advance();
                    if let Token::RawText(expr) = self.peek().clone() {
                        self.advance();
                        nodes.push(TemplateNode::Interpolation(expr));
                        if *self.peek() == Token::RBrace { self.advance(); }
                    }
                }
                Token::HashIdent(kw) => {
                    let kw = kw.clone();
                    self.advance();
                    nodes.push(self.parse_block_tag(&kw)?);
                }
                Token::RawText(t) => {
                    let t = t.clone();
                    self.advance();
                    if !t.trim().is_empty() {
                        nodes.push(TemplateNode::Text(t));
                    }
                }
                _ => { self.advance(); }
            }
        }
        Ok(nodes)
    }
}

fn token_to_str(tok: &Token) -> String {
    match tok {
        Token::Ident(s) => s.clone(),
        Token::StringLit(s) => format!("\"{}\"", s),
        Token::IntLit(n) => n.to_string(),
        Token::FloatLit(f) => f.to_string(),
        Token::BoolLit(b) => b.to_string(),
        Token::LBrace => "{".into(),
        Token::RBrace => "}".into(),
        Token::LParen => "(".into(),
        Token::RParen => ")".into(),
        Token::LBracket => "[".into(),
        Token::RBracket => "]".into(),
        Token::Colon => ":".into(),
        Token::Semicolon => ";".into(),
        Token::Comma => ",".into(),
        Token::Dot => ".".into(),
        Token::Eq => "=".into(),
        Token::EqEq => "==".into(),
        Token::Bang => "!".into(),
        Token::BangEq => "!=".into(),
        Token::Plus => "+".into(),
        Token::Minus => "-".into(),
        Token::Star => "*".into(),
        Token::Slash => "/".into(),
        Token::Arrow => "->".into(),
        Token::FatArrow => "=>".into(),
        Token::AmpAmp => "&&".into(),
        Token::PipePipe => "||".into(),
        Token::Question => "?".into(),
        Token::RawText(s) => s.clone(),
        Token::Return => "return".into(),
        Token::If => "if".into(),
        Token::Else => "else".into(),
        Token::Fn => "fn".into(),
        Token::Component => "component".into(),
        Token::Props => "props".into(),
        Token::State => "state".into(),
        Token::Template => "template".into(),
        _ => String::new(),
    }
}

fn parse_each_header(raw: &str) -> CompileResult<(String, String)> {
    // e.g., "items as item"
    if let Some(idx) = raw.find(" as ") {
        let items = raw[..idx].trim().to_owned();
        let item_name = raw[idx + 4..].trim().to_owned();
        Ok((items, item_name))
    } else {
        Err(CompileError::Parse { msg: format!("invalid #each header: '{}'", raw) })
    }
}

fn parse_activate_header(raw: &str) -> CompileResult<(String, String)> {
    // e.g., `"query string" as results`
    // Strip outer quotes from query
    let raw = raw.trim();
    if let Some(idx) = raw.rfind(" as ") {
        let query_part = raw[..idx].trim();
        let result_name = raw[idx + 4..].trim().to_owned();
        let query = query_part.trim_matches('"').to_owned();
        Ok((query, result_name))
    } else {
        Err(CompileError::Parse { msg: format!("invalid #activate header: '{}'", raw) })
    }
}
