use crate::ast::*;
use crate::LiquidParser;
use crate::Rule;
use pest::Parser;

/// Parses Liquid source code into an AST.
/// 
/// # Arguments
/// * `input` - String slice containing the Liquid source code.
pub fn parse_liquid(input: &str) -> Result<LiquidAST, CompilerError> {
    let mut pairs = LiquidParser::parse(Rule::file, input)
        .map_err(|e| CompilerError::ParseError(format!("{}", e)))?;

    let file_pair = pairs.next().ok_or_else(|| CompilerError::AstConversionError("Empty input".into()))?;
    
    let mut imports = Vec::new();
    let mut classes = Vec::new();

    for pair in file_pair.into_inner() {
        match pair.as_rule() {
            Rule::import_stmt => {
                imports.push(parse_import_stmt(pair)?);
            }
            Rule::class_decl => {
                classes.push(parse_class_decl(pair)?);
            }
            Rule::EOI => {}
            _ => {}
        }
    }

    Ok(LiquidAST { imports, classes })
}

/// Parses an import statement.
fn parse_import_stmt(pair: pest::iterators::Pair<Rule>) -> Result<ImportStmt, CompilerError> {
    let mut inner = pair.into_inner();
    let path_lit = inner.next().ok_or_else(|| CompilerError::AstConversionError("Path not found in import".into()))?;
    let path = path_lit.as_str().trim_matches(|c| c == '"' || c == '\'').to_string();
    Ok(ImportStmt { path })
}

/// Parses a class declaration.
fn parse_class_decl(pair: pest::iterators::Pair<Rule>) -> Result<ClassDecl, CompilerError> {
    let mut inner = pair.into_inner();
    
    let name = inner.next()
        .ok_or_else(|| CompilerError::AstConversionError("Class name not found".into()))?
        .as_str().to_string();
        
    let extends_class = inner.next()
        .ok_or_else(|| CompilerError::AstConversionError("Extended parent class not found".into()))?
        .as_str().to_string();
        
    let body_pair = inner.next()
        .ok_or_else(|| CompilerError::AstConversionError("Class body not found".into()))?;
        
    let mut properties = Vec::new();
    let mut methods = Vec::new();
    let mut render = None;

    for member in body_pair.into_inner() {
        let member_inner = member.into_inner().next().ok_or_else(|| CompilerError::AstConversionError("Empty class member".into()))?;
        match member_inner.as_rule() {
            Rule::property_decl => {
                properties.push(parse_property_decl(member_inner)?);
            }
            Rule::method_decl => {
                methods.push(parse_method_decl(member_inner)?);
            }
            Rule::render_decl => {
                render = Some(parse_render_decl(member_inner)?);
            }
            _ => {}
        }
    }

    let render = if extends_class == "Store" {
        RenderDecl {
            annotation: None,
            type_name: "Widget".to_string(),
            name: "build".to_string(),
            element: Element {
                name: "Empty".to_string(),
                props: Vec::new(),
            },
        }
    } else {
        render.ok_or_else(|| CompilerError::AstConversionError("Class is missing the render build function".into()))?
    };

    Ok(ClassDecl {
        name,
        extends_class,
        properties,
        methods,
        render,
    })
}

/// Parses a property/state declaration.
fn parse_property_decl(pair: pest::iterators::Pair<Rule>) -> Result<PropertyDecl, CompilerError> {
    let mut inner = pair.into_inner();
    let type_pair = inner.next().ok_or_else(|| CompilerError::AstConversionError("Property type not found".into()))?;
    let type_name = type_pair.as_str().to_string();
    
    let name_pair = inner.next().ok_or_else(|| CompilerError::AstConversionError("Property name not found".into()))?;
    let name = name_pair.as_str().to_string();
    
    let value_pair = inner.next().ok_or_else(|| CompilerError::AstConversionError("Property value not found".into()))?;
    let value = parse_value(value_pair)?;
    
    Ok(PropertyDecl { type_name, name, value })
}

/// Parses a method logic declaration.
fn parse_method_decl(pair: pest::iterators::Pair<Rule>) -> Result<MethodDecl, CompilerError> {
    let mut inner = pair.into_inner();
    let type_pair = inner.next().ok_or_else(|| CompilerError::AstConversionError("Method return type not found".into()))?;
    let type_name = type_pair.as_str().to_string();
    
    let name_pair = inner.next().ok_or_else(|| CompilerError::AstConversionError("Method name not found".into()))?;
    let name = name_pair.as_str().to_string();
    
    let mut statements = Vec::new();
    for statement_pair in inner {
        statements.push(parse_statement(statement_pair)?);
    }
    
    Ok(MethodDecl { type_name, name, statements })
}

/// Parses a statement inside a method.
fn parse_statement(pair: pest::iterators::Pair<Rule>) -> Result<Statement, CompilerError> {
    let inner = pair.into_inner().next().ok_or_else(|| CompilerError::AstConversionError("Empty statement".into()))?;
    match inner.as_rule() {
        Rule::value => {
            let val = parse_value(inner)?;
            Ok(Statement::Return(val))
        }
        Rule::statement_expr => {
            let mut expr_inner = inner.into_inner();
            let ident_pair = expr_inner.next().ok_or_else(|| CompilerError::AstConversionError("Identifier not found in statement".into()))?;
            let name = ident_pair.as_str().to_string();
            
            let mut args = None;
            let mut op = None;
            for sub_pair in expr_inner {
                match sub_pair.as_rule() {
                    Rule::statement_args => {
                        let mut args_vec = Vec::new();
                        for val_pair in sub_pair.into_inner() {
                            args_vec.push(parse_value(val_pair)?);
                        }
                        args = Some(args_vec);
                    }
                    Rule::unary_op => {
                        op = Some(sub_pair.as_str().to_string());
                    }
                    _ => {}
                }
            }
            
            Ok(Statement::Expr(StatementExpr { name, args, op }))
        }
        _ => Err(CompilerError::AstConversionError(format!("Unsupported statement type: {:?}", inner.as_rule())))
    }
}

/// Parses the UI render/build declaration.
fn parse_render_decl(pair: pest::iterators::Pair<Rule>) -> Result<RenderDecl, CompilerError> {
    let mut inner = pair.into_inner();
    let mut next_pair = inner.next().ok_or_else(|| CompilerError::AstConversionError("Empty render build".into()))?;
    
    let annotation = if next_pair.as_rule() == Rule::annotation {
        let ann = next_pair.as_str().to_string();
        next_pair = inner.next().ok_or_else(|| CompilerError::AstConversionError("Return type not found after annotation".into()))?;
        Some(ann)
    } else {
        None
    };
    
    let type_name = next_pair.as_str().to_string();
    
    let name_pair = inner.next().ok_or_else(|| CompilerError::AstConversionError("Render build name not found".into()))?;
    let name = name_pair.as_str().to_string();
    
    let element_pair = inner.next().ok_or_else(|| CompilerError::AstConversionError("Render return element not found".into()))?;
    let element = parse_element(element_pair)?;
    
    Ok(RenderDecl {
        annotation,
        type_name,
        name,
        element,
    })
}

/// Parses literal, array, element, or identifier values.
fn parse_value(pair: pest::iterators::Pair<Rule>) -> Result<Value, CompilerError> {
    let inner = pair.into_inner().next().ok_or_else(|| CompilerError::AstConversionError("Empty value".into()))?;
    match inner.as_rule() {
        Rule::string_lit => Ok(Value::StringLit(inner.as_str().trim_matches(|c| c == '"' || c == '\'').to_string())),
        Rule::number_lit => Ok(Value::NumberLit(inner.as_str().to_string())),
        Rule::ident => Ok(Value::Identifier(inner.as_str().to_string())),
        Rule::array => {
            let mut items = Vec::new();
            for item in inner.into_inner() {
                items.push(parse_value(item)?);
            }
            Ok(Value::Array(items))
        }
        Rule::element => {
            let el = parse_element(inner)?;
            Ok(Value::Element(Box::new(el)))
        }
        _ => Err(CompilerError::AstConversionError(format!("Unrecognized value type: {:?}", inner.as_rule()))),
    }
}

/// Parses a declarative UI element and its properties.
fn parse_element(pair: pest::iterators::Pair<Rule>) -> Result<Element, CompilerError> {
    let mut inner = pair.into_inner();
    let name_pair = inner.next().ok_or_else(|| CompilerError::AstConversionError("Element name not found".into()))?;
    let name = name_pair.as_str().to_string();
    
    let mut props = Vec::new();
    if let Some(props_pair) = inner.next() {
        if props_pair.as_rule() == Rule::props {
            for prop_pair in props_pair.into_inner() {
                let mut prop_inner = prop_pair.into_inner();
                let first = prop_inner.next().ok_or_else(|| CompilerError::AstConversionError("Empty element property".into()))?;
                
                let (prop_name, prop_value) = if first.as_rule() == Rule::ident {
                    let n = first.as_str().to_string();
                    let val_pair = prop_inner.next().ok_or_else(|| CompilerError::AstConversionError("Named property missing its value".into()))?;
                    let val = parse_value(val_pair)?;
                    (n, val)
                } else {
                    let val = parse_value(first)?;
                    let n = match &val {
                        Value::StringLit(_) => "text".to_string(),
                        _ => "value".to_string(),
                    };
                    (n, val)
                };
                
                props.push(Prop { name: prop_name, value: prop_value });
            }
        }
    }

    Ok(Element { name, props })
}
