//! Action/expression parsing functions.

use super::*;

/// Parse call_scene.
pub(crate) fn parse_call_scene(pair: Pair<Rule>) -> Result<CallScene, ParseError> {
    let span = Span::from(&pair.as_span());
    let mut target = None;
    let mut args = None;

    for inner in pair.into_inner() {
        match inner.as_rule() {
            Rule::id => {
                target = Some(CallTarget::Static(inner.as_str().to_string()));
            }
            Rule::call_target_expr => {
                if let Some(expr) = parse_expr_from_parts(inner) {
                    target = Some(CallTarget::Dynamic(expr));
                }
            }
            Rule::args => {
                args = Some(parse_args(inner)?);
            }
            _ => {}
        }
    }

    Ok(CallScene {
        target: target.unwrap_or(CallTarget::Static(String::new())),
        args,
        span,
    })
}

/// Parse action_line.
pub(crate) fn parse_action_line(pair: Pair<Rule>) -> Result<ActionLine, ParseError> {
    let span = Span::from(&pair.as_span());
    let mut actor = String::new();
    let mut actions = Vec::new();

    for inner in pair.into_inner() {
        match inner.as_rule() {
            Rule::id => {
                actor = inner.as_str().to_string();
            }
            Rule::actions => {
                actions = parse_actions(inner)?;
            }
            _ => {}
        }
    }

    Ok(ActionLine {
        actor,
        actions,
        span,
    })
}

/// Parse continue_action_line.
pub(crate) fn parse_continue_action_line(pair: Pair<Rule>) -> Result<ContinueAction, ParseError> {
    let span = Span::from(&pair.as_span());
    let mut actions = Vec::new();

    for inner in pair.into_inner() {
        if inner.as_rule() == Rule::actions {
            actions = parse_actions(inner)?;
        }
    }

    Ok(ContinueAction { actions, span })
}

/// Parse actions.
pub(crate) fn parse_actions(pair: Pair<Rule>) -> Result<Vec<Action>, ParseError> {
    let mut actions = Vec::new();

    for inner in pair.into_inner() {
        let action_span = Span::from(&inner.as_span());
        match inner.as_rule() {
            Rule::talk => {
                actions.push(Action::Talk {
                    text: inner.as_str().to_string(),
                    span: action_span,
                });
            }
            Rule::word_ref => {
                for id_inner in inner.into_inner() {
                    if id_inner.as_rule() == Rule::id {
                        actions.push(Action::WordRef {
                            name: id_inner.as_str().to_string(),
                            span: action_span,
                        });
                    }
                }
            }
            Rule::var_ref_local => {
                for var_id_pair in inner.into_inner() {
                    if var_id_pair.as_rule() == Rule::var_id {
                        // var_idの内部構造を確認（idまたはdigit_id）
                        for id_inner in var_id_pair.into_inner() {
                            match id_inner.as_rule() {
                                Rule::id => {
                                    actions.push(Action::VarRef {
                                        name: id_inner.as_str().to_string(),
                                        scope: VarScope::Local,
                                        span: action_span,
                                    });
                                }
                                Rule::digit_id => {
                                    let index = normalize_number_str(id_inner.as_str())
                                        .parse::<u8>()
                                        .unwrap_or(0);
                                    actions.push(Action::VarRef {
                                        name: id_inner.as_str().to_string(),
                                        scope: VarScope::Args(index),
                                        span: action_span,
                                    });
                                }
                                _ => {}
                            }
                        }
                    }
                }
            }
            Rule::var_ref_global => {
                for id_inner in inner.into_inner() {
                    if id_inner.as_rule() == Rule::id {
                        actions.push(Action::VarRef {
                            name: id_inner.as_str().to_string(),
                            scope: VarScope::Global,
                            span: action_span,
                        });
                    }
                }
            }
            Rule::fn_call_local => {
                let (name, args) = parse_fn_call_inner(inner)?;
                actions.push(Action::FnCall {
                    name,
                    args,
                    scope: FnScope::Local,
                    span: action_span,
                });
            }
            Rule::fn_call_global => {
                let (name, args) = parse_fn_call_inner(inner)?;
                actions.push(Action::FnCall {
                    name,
                    args,
                    scope: FnScope::Global,
                    span: action_span,
                });
            }
            Rule::sakura_script => {
                actions.push(Action::SakuraScript {
                    script: inner.as_str().to_string(),
                    span: action_span,
                });
            }
            Rule::at_escape | Rule::dollar_escape | Rule::sakura_escape => {
                actions.push(Action::Escape {
                    sequence: inner.as_str().to_string(),
                    span: action_span,
                });
            }
            _ => {}
        }
    }

    Ok(actions)
}

/// Parse function call inner parts.
pub(crate) fn parse_fn_call_inner(pair: Pair<Rule>) -> Result<(String, Args), ParseError> {
    let mut name = String::new();
    let mut args = Args::empty();

    for inner in pair.into_inner() {
        match inner.as_rule() {
            Rule::id => {
                name = inner.as_str().to_string();
            }
            Rule::args => {
                args = parse_args(inner)?;
            }
            _ => {}
        }
    }

    Ok((name, args))
}

/// Parse args.
pub(crate) fn parse_args(pair: Pair<Rule>) -> Result<Args, ParseError> {
    let span = Span::from(&pair.as_span());
    let mut items = Vec::new();

    for inner in pair.into_inner() {
        match inner.as_rule() {
            Rule::positional_arg => {
                // Collect all terms and operators for this positional argument
                // Grammar: expr = term ~ bin*, where bin = bin_op ~ term
                // Since expr and bin are silent rules, we get term, op, term, op, term...
                if let Some(expr) = parse_expr_from_parts(inner) {
                    items.push(Arg::Positional(expr));
                }
            }
            Rule::key_arg => {
                let (key, value) = parse_key_arg(inner)?;
                items.push(Arg::Keyword { key, value });
            }
            _ => {}
        }
    }

    Ok(Args { items, span })
}

/// Parse an expression from parts (terms and binary operators).
/// Handles the case where expr = term ~ (bin_op ~ term)* expands into multiple pairs.
pub(crate) fn parse_expr_from_parts(pair: Pair<Rule>) -> Option<Expr> {
    let mut terms: Vec<Expr> = Vec::new();
    let mut operators: Vec<BinOp> = Vec::new();

    for expr_inner in pair.into_inner() {
        match expr_inner.as_rule() {
            Rule::add_op => operators.push(BinOp::Add),
            Rule::sub_op => operators.push(BinOp::Sub),
            Rule::mul_op => operators.push(BinOp::Mul),
            Rule::div_op => operators.push(BinOp::Div),
            Rule::modulo_op => operators.push(BinOp::Mod),
            _ => {
                if let Some(term) = try_parse_expr(expr_inner) {
                    terms.push(term);
                }
            }
        }
    }

    if terms.is_empty() {
        return None;
    }

    // Build left-associative binary expression
    let mut result = terms.remove(0);
    for (i, op) in operators.into_iter().enumerate() {
        if i < terms.len() {
            result = Expr::Binary {
                op,
                lhs: Box::new(result),
                rhs: Box::new(terms[i].clone()),
            };
        }
    }

    Some(result)
}

/// Parse key_arg.
pub(crate) fn parse_key_arg(pair: Pair<Rule>) -> Result<(String, Expr), ParseError> {
    let mut key = String::new();
    let mut terms: Vec<Expr> = Vec::new();
    let mut operators: Vec<BinOp> = Vec::new();

    for inner in pair.into_inner() {
        if inner.as_rule() == Rule::key_expr {
            for kv_inner in inner.into_inner() {
                match kv_inner.as_rule() {
                    Rule::id => {
                        // The first id is the key name
                        if key.is_empty() {
                            key = kv_inner.as_str().to_string();
                        }
                    }
                    Rule::add_op => operators.push(BinOp::Add),
                    Rule::sub_op => operators.push(BinOp::Sub),
                    Rule::mul_op => operators.push(BinOp::Mul),
                    Rule::div_op => operators.push(BinOp::Div),
                    Rule::modulo_op => operators.push(BinOp::Mod),
                    _ => {
                        if let Some(expr) = try_parse_expr(kv_inner) {
                            terms.push(expr);
                        }
                    }
                }
            }
        }
    }

    // Build left-associative binary expression
    let value = if terms.is_empty() {
        Expr::BlankString
    } else {
        let mut result = terms.remove(0);
        for (i, op) in operators.into_iter().enumerate() {
            if i < terms.len() {
                result = Expr::Binary {
                    op,
                    lhs: Box::new(result),
                    rhs: Box::new(terms[i].clone()),
                };
            }
        }
        result
    };

    Ok((key, value))
}

/// Try to parse an expression from a pair.
pub(crate) fn try_parse_expr(pair: Pair<Rule>) -> Option<Expr> {
    match pair.as_rule() {
        Rule::number_literal => {
            let normalized = normalize_number_str(pair.as_str());
            if normalized.contains('.') {
                Some(Expr::Float(normalized.parse().ok()?))
            } else {
                Some(Expr::Integer(normalized.parse().ok()?))
            }
        }
        Rule::string_contents => Some(Expr::String(pair.as_str().to_string())),
        Rule::string_blank => Some(Expr::BlankString),
        Rule::var_ref_local => {
            for var_id_pair in pair.into_inner() {
                if var_id_pair.as_rule() == Rule::var_id {
                    // var_idの内部構造を確認（idまたはdigit_id）
                    for id_inner in var_id_pair.into_inner() {
                        match id_inner.as_rule() {
                            Rule::id => {
                                return Some(Expr::VarRef {
                                    name: id_inner.as_str().to_string(),
                                    scope: VarScope::Local,
                                });
                            }
                            Rule::digit_id => {
                                let index = normalize_number_str(id_inner.as_str())
                                    .parse::<u8>()
                                    .unwrap_or(0);
                                return Some(Expr::VarRef {
                                    name: id_inner.as_str().to_string(),
                                    scope: VarScope::Args(index),
                                });
                            }
                            _ => {}
                        }
                    }
                }
            }
            None
        }
        Rule::var_ref_global => {
            for inner in pair.into_inner() {
                if inner.as_rule() == Rule::id {
                    return Some(Expr::VarRef {
                        name: inner.as_str().to_string(),
                        scope: VarScope::Global,
                    });
                }
            }
            None
        }
        Rule::fn_call_local => {
            let (name, args) = parse_fn_call_inner(pair).ok()?;
            Some(Expr::FnCall {
                name,
                args,
                scope: FnScope::Local,
            })
        }
        Rule::fn_call_global => {
            let (name, args) = parse_fn_call_inner(pair).ok()?;
            Some(Expr::FnCall {
                name,
                args,
                scope: FnScope::Global,
            })
        }
        Rule::paren_expr => {
            for inner in pair.into_inner() {
                if let Some(expr) = try_parse_expr(inner) {
                    return Some(Expr::Paren(Box::new(expr)));
                }
            }
            None
        }
        Rule::add_op | Rule::sub_op | Rule::mul_op | Rule::div_op | Rule::modulo_op => {
            // Binary operators are handled at a higher level
            None
        }
        _ => {
            // Try recursively for nested expressions
            for inner in pair.into_inner() {
                if let Some(expr) = try_parse_expr(inner) {
                    return Some(expr);
                }
            }
            None
        }
    }
}
