use super::globals::resolve_bridge_global;
use super::match_expr;
use super::merge::new_block;
use super::ternary;
use super::throw_lower::lower_throw;
use super::BridgeEnv;
use crate::ast::Span;
use crate::mir::{BasicBlockId, ConstValue, EffectMask, MirFunction, MirInstruction, ValueId};
use std::collections::BTreeMap;

use super::super::ast::ExprV0;

pub(super) trait VarScope {
    fn resolve(
        &mut self,
        env: &BridgeEnv,
        f: &mut MirFunction,
        cur_bb: BasicBlockId,
        name: &str,
    ) -> Result<Option<ValueId>, String>;
}

pub(super) struct NoVars;
impl VarScope for NoVars {
    fn resolve(
        &mut self,
        _env: &BridgeEnv,
        _f: &mut MirFunction,
        _cur_bb: BasicBlockId,
        name: &str,
    ) -> Result<Option<ValueId>, String> {
        Err(format!("undefined variable in this context: {}", name))
    }
}

pub(super) struct MapVars<'a> {
    vars: &'a mut BTreeMap<String, ValueId>,
}
impl<'a> MapVars<'a> {
    fn new(vars: &'a mut BTreeMap<String, ValueId>) -> Self {
        Self { vars }
    }
}
impl<'a> VarScope for MapVars<'a> {
    fn resolve(
        &mut self,
        env: &BridgeEnv,
        f: &mut MirFunction,
        cur_bb: BasicBlockId,
        name: &str,
    ) -> Result<Option<ValueId>, String> {
        if let Some(&vid) = self.vars.get(name) {
            return Ok(Some(vid));
        }

        // Bridge 固有のグローバル解決（imports/hostbridge/env/me dummy）は専用モジュールに委譲
        if let Some(vid) = resolve_bridge_global(name, env, f, cur_bb, self.vars)? {
            return Ok(Some(vid));
        }

        Ok(None)
    }
}

pub(super) fn lower_expr_with_scope<S: VarScope>(
    env: &BridgeEnv,
    f: &mut MirFunction,
    cur_bb: BasicBlockId,
    e: &ExprV0,
    vars: &mut S,
) -> Result<(ValueId, BasicBlockId), String> {
    match e {
        ExprV0::Int { value } => {
            let ival: i64 = if let Some(n) = value.as_i64() {
                n
            } else if let Some(s) = value.as_str() {
                s.parse().map_err(|_| "invalid int literal")?
            } else {
                return Err("invalid int literal".into());
            };
            let dst = f.next_value_id();
            if let Some(bb) = f.get_block_mut(cur_bb) {
                bb.add_instruction(MirInstruction::Const {
                    dst,
                    value: ConstValue::Integer(ival),
                });
            }
            Ok((dst, cur_bb))
        }
        ExprV0::Str { value } => {
            let dst = f.next_value_id();
            if let Some(bb) = f.get_block_mut(cur_bb) {
                bb.add_instruction(MirInstruction::Const {
                    dst,
                    value: ConstValue::String(value.clone()),
                });
            }
            Ok((dst, cur_bb))
        }
        ExprV0::Bool { value } => {
            let dst = f.next_value_id();
            if let Some(bb) = f.get_block_mut(cur_bb) {
                bb.add_instruction(MirInstruction::Const {
                    dst,
                    value: ConstValue::Bool(*value),
                });
            }
            Ok((dst, cur_bb))
        }
        ExprV0::Null => {
            let dst = f.next_value_id();
            if let Some(bb) = f.get_block_mut(cur_bb) {
                bb.add_instruction(MirInstruction::Const {
                    dst,
                    value: ConstValue::Null,
                });
            }
            Ok((dst, cur_bb))
        }
        ExprV0::Binary { op, lhs, rhs } => {
            let (l, cur_after_l) = lower_expr_with_scope(env, f, cur_bb, lhs, vars)?;
            let (r, cur_after_r) = lower_expr_with_scope(env, f, cur_after_l, rhs, vars)?;
            let bop = match crate::mir::ssot::binop_lower::parse_binop_str(op) {
                Some(b) => b,
                None => return Err("unsupported op".into()),
            };
            let dst = crate::mir::ssot::binop_lower::emit_binop_func(f, cur_after_r, bop, l, r);
            Ok((dst, cur_after_r))
        }
        ExprV0::Extern {
            iface,
            method,
            args,
        } => {
            let (arg_ids, cur2) = lower_args_with_scope(env, f, cur_bb, args, vars)?;
            let dst = f.next_value_id();
            if let Some(bb) = f.get_block_mut(cur2) {
                bb.add_instruction(MirInstruction::ExternCall {
                    dst: Some(dst),
                    iface_name: iface.clone(),
                    method_name: method.clone(),
                    args: arg_ids,
                    effects: EffectMask::IO,
                });
            }
            Ok((dst, cur2))
        }
        ExprV0::Compare { op, lhs, rhs } => {
            let (l, cur_after_l) = lower_expr_with_scope(env, f, cur_bb, lhs, vars)?;
            let (r, cur_after_r) = lower_expr_with_scope(env, f, cur_after_l, rhs, vars)?;
            let cop = match op.as_str() {
                "==" => crate::mir::CompareOp::Eq,
                "!=" => crate::mir::CompareOp::Ne,
                "<" => crate::mir::CompareOp::Lt,
                "<=" => crate::mir::CompareOp::Le,
                ">" => crate::mir::CompareOp::Gt,
                ">=" => crate::mir::CompareOp::Ge,
                _ => return Err("unsupported compare op".into()),
            };
            let dst = f.next_value_id();
            crate::mir::ssot::cf_common::emit_compare_func(f, cur_after_r, dst, cop, l, r);
            Ok((dst, cur_after_r))
        }
        ExprV0::Logical { op, lhs, rhs } => {
            let (l, cur_after_l) = lower_expr_with_scope(env, f, cur_bb, lhs, vars)?;
            let rhs_bb = new_block(f);
            let fall_bb = new_block(f);
            let merge_bb = new_block(f);
            let is_and = matches!(op.as_str(), "&&" | "and");
            if is_and {
                crate::mir::ssot::cf_common::set_branch(f, cur_after_l, l, rhs_bb, fall_bb);
            } else {
                crate::mir::ssot::cf_common::set_branch(f, cur_after_l, l, fall_bb, rhs_bb);
            }
            // ARCHIVED: JIT events moved to archive/jit-cranelift/ during Phase 15
            // crate::jit::events::emit_lower(
            //     serde_json::json!({ "id":"shortcircuit","op": if is_and {"and"} else {"or"},"rhs_bb":rhs_bb.0,"fall_bb":fall_bb.0,"merge_bb":merge_bb.0 }),
            //     "shortcircuit",
            //     "<json_v0>",
            // );
            let cdst = f.next_value_id();
            if let Some(bb) = f.get_block_mut(fall_bb) {
                let cval = if is_and {
                    ConstValue::Bool(false)
                } else {
                    ConstValue::Bool(true)
                };
                bb.add_instruction(MirInstruction::Const {
                    dst: cdst,
                    value: cval,
                });
            }
            crate::mir::ssot::cf_common::set_jump(f, fall_bb, merge_bb);
            let (rval, rhs_end) = lower_expr_with_scope(env, f, rhs_bb, rhs, vars)?;
            if let Some(bb) = f.get_block_mut(rhs_end) {
                if !bb.is_terminated() {
                    crate::mir::ssot::cf_common::set_jump(f, rhs_end, merge_bb);
                }
            }
            let out = f.next_value_id();
            // フェーズM.2: PHI統一処理（no_phi分岐削除）
            let mut inputs: Vec<(BasicBlockId, ValueId)> = vec![(fall_bb, cdst)];
            if rhs_end != fall_bb {
                inputs.push((rhs_end, rval));
            } else {
                inputs.push((fall_bb, rval));
            }
            crate::mir::ssot::cf_common::insert_phi_at_head_spanned(
                f,
                merge_bb,
                out,
                inputs,
                Span::unknown(),
            );
            Ok((out, merge_bb))
        }
        ExprV0::Call { name, args } => {
            if name == "array.of" {
                let arr = f.next_value_id();
                if let Some(bb) = f.get_block_mut(cur_bb) {
                    bb.add_instruction(MirInstruction::NewBox {
                        dst: arr,
                        box_type: "ArrayBox".into(),
                        args: vec![],
                    });
                }
                let mut cur = cur_bb;
                for e in args {
                    let (v, c) = lower_expr_with_scope(env, f, cur, e, vars)?;
                    cur = c;
                    let tmp = f.next_value_id();
                    if let Some(bb) = f.get_block_mut(cur) {
                        bb.add_instruction(MirInstruction::BoxCall {
                            dst: Some(tmp),
                            box_val: arr,
                            method: "push".into(),
                            method_id: None,
                            args: vec![v],
                            effects: EffectMask::READ,
                        });
                    }
                }
                return Ok((arr, cur));
            }
            if name == "map.of" {
                let mapv = f.next_value_id();
                if let Some(bb) = f.get_block_mut(cur_bb) {
                    bb.add_instruction(MirInstruction::NewBox {
                        dst: mapv,
                        box_type: "MapBox".into(),
                        args: vec![],
                    });
                }
                let mut cur = cur_bb;
                let mut it = args.iter();
                while let Some(k) = it.next() {
                    if let Some(v) = it.next() {
                        let (kv, cur2) = lower_expr_with_scope(env, f, cur, k, vars)?;
                        cur = cur2;
                        let (vv, cur3) = lower_expr_with_scope(env, f, cur, v, vars)?;
                        cur = cur3;
                        let tmp = f.next_value_id();
                        if let Some(bb) = f.get_block_mut(cur) {
                            bb.add_instruction(MirInstruction::BoxCall {
                                dst: Some(tmp),
                                box_val: mapv,
                                method: "set".into(),
                                method_id: None,
                                args: vec![kv, vv],
                                effects: EffectMask::READ,
                            });
                        }
                    } else {
                        break;
                    }
                }
                return Ok((mapv, cur));
            }
            let (arg_ids, cur) = lower_args_with_scope(env, f, cur_bb, args, vars)?;
            let fun_val = f.next_value_id();
            if let Some(bb) = f.get_block_mut(cur) {
                bb.add_instruction(MirInstruction::Const {
                    dst: fun_val,
                    value: ConstValue::String(name.clone()),
                });
            }
            let dst = f.next_value_id();
            if let Some(bb) = f.get_block_mut(cur) {
                bb.add_instruction(MirInstruction::Call {
                    dst: Some(dst),
                    func: fun_val,
                    callee: None, // JSON bridge - use legacy resolution
                    args: arg_ids,
                    effects: EffectMask::READ,
                });
            }
            Ok((dst, cur))
        }
        ExprV0::Method { recv, method, args } => {
            let recv_is_console_new =
                matches!(&**recv, ExprV0::New { class, .. } if class == "ConsoleBox");
            if recv_is_console_new && (method == "println" || method == "print" || method == "log")
            {
                let (arg_ids, cur2) = lower_args_with_scope(env, f, cur_bb, args, vars)?;
                let dst = f.next_value_id();
                if let Some(bb) = f.get_block_mut(cur2) {
                    bb.add_instruction(MirInstruction::ExternCall {
                        dst: Some(dst),
                        iface_name: "env.console".into(),
                        method_name: "log".into(),
                        args: arg_ids,
                        effects: EffectMask::READ,
                    });
                }
                return Ok((dst, cur2));
            }
            // Phase 25.1b: Handle env.box_introspect.kind(value) pattern
            // Pattern: Method { recv: Method { recv: Var("env"), method: "box_introspect" }, method: "kind", args }
            if let ExprV0::Method {
                recv: inner_recv,
                method: inner_method,
                args: inner_args,
            } = &**recv
            {
                if matches!(&**inner_recv, ExprV0::Var { name } if name == "env")
                    && inner_method == "box_introspect"
                    && inner_args.is_empty()
                {
                    // Lower args for the final method call
                    let (arg_ids, cur2) = lower_args_with_scope(env, f, cur_bb, args, vars)?;
                    let dst = f.next_value_id();

                    if let Some(bb) = f.get_block_mut(cur2) {
                        bb.add_instruction(MirInstruction::ExternCall {
                            dst: Some(dst),
                            iface_name: "env.box_introspect".into(),
                            method_name: method.clone(),
                            args: arg_ids,
                            effects: EffectMask::READ,
                        });
                    }
                    return Ok((dst, cur2));
                }
            }
            let (recv_v, cur) = lower_expr_with_scope(env, f, cur_bb, recv, vars)?;
            let (arg_ids, cur2) = lower_args_with_scope(env, f, cur, args, vars)?;
            let dst = f.next_value_id();
            if let Some(bb) = f.get_block_mut(cur2) {
                bb.add_instruction(MirInstruction::BoxCall {
                    dst: Some(dst),
                    box_val: recv_v,
                    method: method.clone(),
                    method_id: None,
                    args: arg_ids,
                    effects: EffectMask::READ,
                });
            }
            Ok((dst, cur2))
        }
        ExprV0::New { class, args } => {
            let (arg_ids, cur) = lower_args_with_scope(env, f, cur_bb, args, vars)?;
            let dst = f.next_value_id();
            if let Some(bb) = f.get_block_mut(cur) {
                bb.add_instruction(MirInstruction::NewBox {
                    dst,
                    box_type: class.clone(),
                    args: arg_ids,
                });
            }
            Ok((dst, cur))
        }
        ExprV0::Var { name } => match vars.resolve(env, f, cur_bb, name)? {
            Some(v) => Ok((v, cur_bb)),
            None => Err(format!("undefined variable: {}", name)),
        },
        ExprV0::Throw { expr } => {
            let (exc, cur) = lower_expr_with_scope(env, f, cur_bb, expr, vars)?;
            Ok(lower_throw(env, f, cur, exc))
        }
        ExprV0::Ternary { cond, then, r#else } => {
            ternary::lower_ternary_expr_with_scope(env, f, cur_bb, cond, then, r#else, vars)
        }
        ExprV0::Match {
            scrutinee,
            arms,
            r#else,
        } => match_expr::lower_match_expr_with_scope(env, f, cur_bb, scrutinee, arms, r#else, vars),
    }
}

fn lower_args_with_scope<S: VarScope>(
    env: &BridgeEnv,
    f: &mut MirFunction,
    cur_bb: BasicBlockId,
    args: &[ExprV0],
    scope: &mut S,
) -> Result<(Vec<ValueId>, BasicBlockId), String> {
    let mut out = Vec::with_capacity(args.len());
    let mut cur = cur_bb;
    for a in args {
        let (v, c) = lower_expr_with_scope(env, f, cur, a, scope)?;
        out.push(v);
        cur = c;
    }
    Ok((out, cur))
}

#[allow(dead_code)]
fn lower_expr(
    env: &BridgeEnv,
    f: &mut MirFunction,
    cur_bb: BasicBlockId,
    e: &ExprV0,
) -> Result<(ValueId, BasicBlockId), String> {
    let mut scope = NoVars;
    lower_expr_with_scope(env, f, cur_bb, e, &mut scope)
}

pub(super) fn lower_expr_with_vars(
    env: &BridgeEnv,
    f: &mut MirFunction,
    cur_bb: BasicBlockId,
    e: &ExprV0,
    vars: &mut BTreeMap<String, ValueId>,
) -> Result<(ValueId, BasicBlockId), String> {
    let mut scope = MapVars::new(vars);
    lower_expr_with_scope(env, f, cur_bb, e, &mut scope)
}

#[allow(dead_code)]
fn lower_args(
    env: &BridgeEnv,
    f: &mut MirFunction,
    cur_bb: BasicBlockId,
    args: &[ExprV0],
) -> Result<(Vec<ValueId>, BasicBlockId), String> {
    let mut scope = NoVars;
    lower_args_with_scope(env, f, cur_bb, args, &mut scope)
}

pub(super) fn lower_args_with_vars(
    env: &BridgeEnv,
    f: &mut MirFunction,
    cur_bb: BasicBlockId,
    args: &[ExprV0],
    vars: &mut BTreeMap<String, ValueId>,
) -> Result<(Vec<ValueId>, BasicBlockId), String> {
    let mut scope = MapVars::new(vars);
    lower_args_with_scope(env, f, cur_bb, args, &mut scope)
}
