use super::{
    BasicBlockId, EffectMask, FunctionSignature, MirInstruction, MirModule, MirType, ValueId,
};
use crate::ast::ASTNode;
use crate::config;

// Lifecycle routines extracted from builder.rs
fn has_main_static(ast: &ASTNode) -> bool {
    use crate::ast::ASTNode as N;
    if let N::Program { statements, .. } = ast {
        for st in statements {
            if let N::BoxDeclaration {
                name,
                methods,
                is_static,
                ..
            } = st
            {
                if *is_static && name == "Main" {
                    if let Some(m) = methods.get("main") {
                        if let N::FunctionDeclaration { .. } = m {
                            return true;
                        }
                    }
                }
            }
        }
    }
    false
}

// Phase 65.5: 型ヒントポリシーを箱化モジュールから使用
//
// 60 行削減！get_phi_type_hint() と is_type_hint_target() を
// type_hint_policy モジュールに移動。
//
// 箱化の利点：
// - ✅ 単一責務：ポリシー判定のみ
// - ✅ テスト可能：各 Phase 独立テスト
// - ✅ 拡張容易：Phase 66+ で P3-C 追加が簡単
use crate::mir::join_ir::lowering::type_hint_policy::TypeHintPolicy;
// Phase 67: P3-C ジェネリック型推論箱
use crate::mir::join_ir::lowering::generic_type_resolver::GenericTypeResolver;
// Phase 83: P3-D 既知メソッド戻り値型推論箱
use crate::mir::join_ir::lowering::method_return_hint::MethodReturnHintBox;
// Phase 84-2: Copy命令型伝播箱（ChatGPT Pro設計）
// Phase 84-3: PHI + Copy グラフ型推論箱（ChatGPT Pro設計）
use crate::mir::phi_core::phi_type_resolver::PhiTypeResolver;

// Phase 82: dev ガード用ヘルパー - Case 分類ロジック統一化
//
// infer_type_from_phi_with_hint() の 2 つの callsite で重複していた
// Case 判定ロジックを DRY 化。
//
// Case 分類:
// - Case A: hint 付き（GenericTypeResolver 不要）
// - Case B: P1/P2/P3-A/B で hint 失敗（理論上不可）
// - Case D: P3-C で GenericTypeResolver 失敗（PHI 走査フォールバック）
//
// Note: dev フラグで制御されるので、#[cfg] は不要（環境変数で制御）
#[allow(dead_code)]
fn classify_phi_fallback_case(hint: Option<&MirType>, function_name: &str) -> &'static str {
    if hint.is_some() {
        "Case A (hint付き)"
    } else if TypeHintPolicy::is_target(function_name) {
        "Case B (P1/P2/P3-A/B hint失敗)"
    } else {
        "Case D (P3-C GenericTypeResolver失敗)"
    }
}

impl super::MirBuilder {
    /// Unified declaration indexing (Phase A): collect symbols before lowering
    /// - user_defined_boxes: non-static Box names (for NewBox birth() skip)
    /// - static_method_index: name -> [(BoxName, arity)] (for bare-call fallback)
    fn index_declarations(&mut self, node: &ASTNode) {
        match node {
            ASTNode::Program { statements, .. } => {
                for st in statements {
                    self.index_declarations(st);
                }
            }
            ASTNode::BoxDeclaration {
                name,
                fields,          // Phase 285LLVM-1.1: Extract fields
                methods,
                is_static,
                ..
            } => {
                if !*is_static {
                    // Phase 285LLVM-1.1: Register instance box with field information
                    self.comp_ctx.register_user_box_with_fields(name.clone(), fields.clone());
                } else {
                    // Static box: no fields
                    self.comp_ctx.register_user_box(name.clone());
                    for (mname, mast) in methods {
                        if let ASTNode::FunctionDeclaration { params, .. } = mast {
                            self.comp_ctx
                                .static_method_index
                                .entry(mname.clone())
                                .or_insert_with(Vec::new)
                                .push((name.clone(), params.len()));
                        }
                    }
                }
            }
            _ => {}
        }
    }
    pub(super) fn prepare_module(&mut self) -> Result<(), String> {
        let mut module = MirModule::new("main".to_string());
        module.metadata.source_file = self.current_source_file();
        let main_signature = FunctionSignature {
            name: "main".to_string(),
            params: vec![],
            return_type: MirType::Void,
            effects: EffectMask::PURE,
        };

        let entry_block = self.next_block_id();
        let mut main_function = self.new_function_with_metadata(main_signature, entry_block);
        main_function.metadata.is_entry_point = true;

        self.current_module = Some(module);
        // Phase 136 Step 3/7: Use scope_ctx as SSOT
        self.scope_ctx.current_function = Some(main_function);
        self.current_block = Some(entry_block);

        // 関数スコープの SlotRegistry を初期化するよ（観測専用）。
        // main 関数用のスロット登録箱として使う想定だよ。
        self.comp_ctx.current_slot_registry =
            Some(crate::mir::region::function_slot_registry::FunctionSlotRegistry::new());

        // Region 観測レイヤ: main 関数の FunctionRegion を 1 つ作っておくよ。
        crate::mir::region::observer::observe_function_region(self);

        // Hint: scope enter at function entry (id=0 for main)
        self.hint_scope_enter(0);

        if std::env::var("NYASH_BUILDER_SAFEPOINT_ENTRY")
            .ok()
            .as_deref()
            == Some("1")
        {
            self.emit_instruction(MirInstruction::Safepoint)?;
        }

        Ok(())
    }

    pub(super) fn lower_root(&mut self, ast: ASTNode) -> Result<ValueId, String> {
        // Pre-index static methods to enable safe fallback for bare calls in using-prepended code
        let snapshot = ast.clone();
        // Phase A: collect declarations in one pass (symbols available to lowering)
        self.index_declarations(&snapshot);

        // Decide root mode (App vs Script) once per module based on presence of static box Main.main
        // true  => App mode (Main.main is entry)
        // false => Script/Test mode (top-level Program runs sequentially)
        let is_app_mode = self
            .root_is_app_mode
            .unwrap_or_else(|| has_main_static(&snapshot));
        self.root_is_app_mode = Some(is_app_mode);

        // Phase B: top-level program lowering with declaration-first pass
        match ast {
            ASTNode::Program { statements, .. } => {
                use crate::ast::ASTNode as N;
                // First pass: lower declarations (static boxes except Main, and instance boxes)
                let mut main_static: Option<(String, std::collections::HashMap<String, ASTNode>)> =
                    None;
                for st in &statements {
                    if let N::BoxDeclaration {
                        name,
                        methods,
                        is_static,
                        fields,
                        constructors,
                        weak_fields,
                        ..
                    } = st
                    {
                        if *is_static {
                            if name == "Main" {
                                main_static = Some((name.clone(), methods.clone()));
                            } else {
                                // Script/Test モードでは static box の lowering は exprs.rs 側に任せる
                                if is_app_mode {
                                    // Dev: trace which static box is being lowered (env-gated)
                                    self.trace_compile(format!("lower static box {}", name));
                                    // 🎯 箱理論: 各static boxに専用のコンパイルコンテキストを作成
                                    // これにより、using文や前のboxからのメタデータ汚染を構造的に防止
                                    // スコープを抜けると自動的にコンテキストが破棄される
                                    {
                                        let ctx = super::context::BoxCompilationContext::new();
                                        self.comp_ctx.compilation_context = Some(ctx);

                                        // Lower all static methods into standalone functions: BoxName.method/Arity
                                        for (mname, mast) in methods.iter() {
                                            if let N::FunctionDeclaration { params, body, .. } =
                                                mast
                                            {
                                                let func_name = format!(
                                                    "{}.{}{}",
                                                    name,
                                                    mname,
                                                    format!("/{}", params.len())
                                                );
                                                self.lower_static_method_as_function(
                                                    func_name,
                                                    params.clone(),
                                                    body.clone(),
                                                )?;
                                                self.comp_ctx
                                                    .static_method_index
                                                    .entry(mname.clone())
                                                    .or_insert_with(Vec::new)
                                                    .push((name.clone(), params.len()));
                                            }
                                        }
                                    }

                                    // 🎯 箱理論: コンテキストをクリア（スコープ終了で自動破棄）
                                    // これにより、次のstatic boxは汚染されていない状態から開始される
                                    self.comp_ctx.compilation_context = None;
                                }
                            }
                        } else {
                            // Instance box: register type and lower instance methods/ctors as functions
                            // Phase 285LLVM-1.1: Register with field information for LLVM harness
                            self.comp_ctx.register_user_box_with_fields(name.clone(), fields.clone());
                            self.build_box_declaration(
                                name.clone(),
                                methods.clone(),
                                fields.clone(),
                                weak_fields.clone(),
                            )?;
                            for (ctor_key, ctor_ast) in constructors.iter() {
                                if let N::FunctionDeclaration { params, body, .. } = ctor_ast {
                                    // Keep constructor function name as "Box.birth/N" where ctor_key already encodes arity.
                                    // ctor_key format comes from parser as "birth/<arity>".
                                    let func_name = format!("{}.{}", name, ctor_key);
                                    self.lower_method_as_function(
                                        func_name,
                                        name.clone(),
                                        params.clone(),
                                        body.clone(),
                                    )?;
                                }
                            }
                            for (mname, mast) in methods.iter() {
                                if let N::FunctionDeclaration {
                                    params,
                                    body,
                                    is_static,
                                    ..
                                } = mast
                                {
                                    if !*is_static {
                                        let func_name = format!(
                                            "{}.{}{}",
                                            name,
                                            mname,
                                            format!("/{}", params.len())
                                        );
                                        self.lower_method_as_function(
                                            func_name,
                                            name.clone(),
                                            params.clone(),
                                            body.clone(),
                                        )?;
                                    }
                                }
                            }
                        }
                    }
                }

                // Second pass: mode-dependent entry lowering
                if is_app_mode {
                    // App モード: Main.main をエントリとして扱う
                    if let Some((box_name, methods)) = main_static {
                        self.build_static_main_box(box_name, methods)
                    } else {
                        // 理論上は起こりにくいが、安全のため Script モードと同じフォールバックにする
                        self.cf_block(statements)
                    }
                } else {
                    // Script/Test モード: トップレベル Program をそのまま順次実行
                    self.cf_block(statements)
                }
            }
            other => self.build_expression(other),
        }
    }

    pub(super) fn finalize_module(&mut self, result_value: ValueId) -> Result<MirModule, String> {
        // Hint: scope leave at function end (id=0 for main)
        self.hint_scope_leave(0);
        if let Some(block_id) = self.current_block {
            if let Some(ref mut function) = self.scope_ctx.current_function {
                if let Some(block) = function.get_block_mut(block_id) {
                    if !block.is_terminated() {
                        block.add_instruction(MirInstruction::Return {
                            value: Some(result_value),
                        });
                    }
                    if let Some(mt) = self.type_ctx.value_types.get(&result_value).cloned() {
                        function.signature.return_type = mt;
                    }
                }
            }
        }

        let mut module = self.current_module.take().unwrap();
        // Phase 136 Step 3/7: Take from scope_ctx (SSOT)
        let mut function = self.scope_ctx.current_function.take().unwrap();
        // Phase 279 P0: SSOT type propagation pipeline
        //
        // 全ての型伝播処理を1つの入口（SSOT）に統一。
        // 順序固定: Copy → BinOp → Copy → PHI
        // lifecycle.rs と joinir_function_converter.rs の両方がこのパイプラインを呼ぶ。
        use crate::mir::type_propagation::TypePropagationPipeline;
        TypePropagationPipeline::run(&mut function, &mut self.type_ctx.value_types)?;

        // Phase 84-5 guard hardening: ensure call/await results are registered in `value_types`
        // before return type inference. This avoids "impossible" debug panics when the builder
        // emitted a value-producing instruction without annotating its dst type.
        self.annotate_missing_result_types_from_calls_and_await(&function, &module);

        // Phase 131-9: Update function metadata with corrected types
        // MUST happen after PHI type correction above AND BinOp re-propagation
        function.metadata.value_types = self.type_ctx.value_types.clone();

        // Phase 82-5: lifecycle.rs バグ修正 - terminator の Return のみをチェック
        //   問題: instructions を先に走査すると、中間値（const void 等）を誤って推論対象にしてしまう
        //   解決: terminator の Return のみをチェックし、実際の戻り値を正しく推論する
        if matches!(
            function.signature.return_type,
            super::MirType::Void | super::MirType::Unknown
        ) {
            let mut inferred: Option<super::MirType> = None;
            for (_bid, bb) in function.blocks.iter() {
                // Phase 82-5: instructions 走査を削除、terminator の Return のみをチェック
                if let Some(super::MirInstruction::Return { value: Some(v) }) = &bb.terminator {
                    if let Some(mt) = self.type_ctx.value_types.get(v).cloned() {
                        inferred = Some(mt);
                        break;
                    }
                    // Phase 65.5: TypeHintPolicy 使用（箱化モジュール）
                    // Phase 67: P3-C 経路を GenericTypeResolver に委譲
                    let hint = if TypeHintPolicy::is_target(&function.signature.name) {
                        TypeHintPolicy::extract_phi_type_hint(&function, *v)
                    } else {
                        None
                    };
                    // Phase 83: P3-D 既知メソッド戻り値型推論（P3-C より先に試行）
                    //
                    // P3-D は「既知メソッドの戻り値型」を直接推論する。
                    // BoxCall の method 名から TypeAnnotationBox と同じマッピングで型を取得。
                    if hint.is_none() {
                        if let Some(mt) = MethodReturnHintBox::resolve_for_return(
                            &function,
                            *v,
                            &self.type_ctx.value_types,
                        ) {
                            if std::env::var("NYASH_P3D_DEBUG").is_ok() {
                                eprintln!(
                                    "[lifecycle/p3d] {} type inferred via MethodReturnHintBox: {:?}",
                                    function.signature.name, mt
                                );
                            }
                            inferred = Some(mt);
                            break;
                        }
                    }
                    // Phase 84-3: P4 PHI + Copy グラフ型推論（P3-C より先に試行）
                    //
                    // PHI + Copy の小グラフを DFS 探索し、1 種類の型に収束する場合のみ返す。
                    // これにより Loop edge copy / If merge 後の型推論が解決できる。
                    if hint.is_none() {
                        let phi_resolver =
                            PhiTypeResolver::new(&function, &self.type_ctx.value_types);
                        if let Some(mt) = phi_resolver.resolve(*v) {
                            if std::env::var("NYASH_P4_DEBUG").is_ok() {
                                eprintln!(
                                    "[lifecycle/p4] {} type inferred via PhiTypeResolver: {:?}",
                                    function.signature.name, mt
                                );
                            }
                            inferred = Some(mt);
                            break;
                        }
                    }
                    // Phase 67: P3-C 対象なら GenericTypeResolver を優先使用
                    if hint.is_none() && TypeHintPolicy::is_p3c_target(&function.signature.name) {
                        if let Some(mt) = GenericTypeResolver::resolve_from_phi(
                            &function,
                            *v,
                            &self.type_ctx.value_types,
                        ) {
                            if std::env::var("NYASH_P3C_DEBUG").is_ok() {
                                eprintln!(
                                    "[lifecycle/p3c] {} type inferred via GenericTypeResolver: {:?}",
                                    function.signature.name, mt
                                );
                            }
                            inferred = Some(mt);
                            break;
                        }
                    }
                    // Phase 84-5: if_phi.rs 完全削除後の安全ガード
                    #[cfg(debug_assertions)]
                    {
                        panic!(
                            "[phase84-5] Type inference failed for {:?} in function {}\n\
                             This should not happen after Phase 84-4 completion.\n\
                             Please check: PhiTypeResolver, BoxCall type registration, CopyTypePropagator",
                            v, function.signature.name
                        );
                    }

                    #[cfg(not(debug_assertions))]
                    {
                        eprintln!(
                            "[phase84-5/warning] Type inference failed for {:?} in {}, using Unknown fallback",
                            v, function.signature.name
                        );
                        inferred = Some(MirType::Unknown);
                    }
                }
            }
            if let Some(mt) = inferred {
                function.signature.return_type = mt;
            }
        }
        // Dev-only verify: NewBox → birth() invariant (warn if missing)
        // Stage‑B 用トグル: NYASH_STAGEB_DEV_VERIFY=0 のときは StageBDriverBox だけ警告をスキップする。
        if crate::config::env::using_is_dev() {
            let _stageb_dev_verify_on = config::env::stageb_dev_verify_enabled();
            let mut warn_count = 0usize;
            for (_bid, bb) in function.blocks.iter() {
                let insns = &bb.instructions;
                let mut idx = 0usize;
                while idx < insns.len() {
                    if let MirInstruction::NewBox {
                        dst,
                        box_type,
                        args,
                    } = &insns[idx]
                    {
                        // Phase 71-SSA 71-11.2: StageBDriverBox is a static box → skip birth warning unconditionally
                        // Static boxes don't follow NewBox→birth pattern by design
                        if box_type == "StageBDriverBox" {
                            idx += 1;
                            continue;
                        }
                        // Skip StringBox (literal optimization path)
                        if box_type != "StringBox" {
                            let expect_tail = format!("{}.birth/{}", box_type, args.len());
                            // Look ahead up to 3 instructions for either BoxCall("birth") on dst or Global(expect_tail)
                            let mut ok = false;
                            let mut j = idx + 1;
                            let mut last_const_name: Option<String> = None;
                            while j < insns.len() && j <= idx + 3 {
                                match &insns[j] {
                                    MirInstruction::BoxCall {
                                        box_val, method, ..
                                    } => {
                                        if method == "birth" && box_val == dst {
                                            ok = true;
                                            break;
                                        }
                                    }
                                    MirInstruction::Const { value, .. } => {
                                        if let super::ConstValue::String(s) = value {
                                            last_const_name = Some(s.clone());
                                        }
                                    }
                                    MirInstruction::Call { func: _, .. } => {
                                        // If immediately preceded by matching Const String, accept
                                        if let Some(prev) = last_const_name.as_ref() {
                                            if prev == &expect_tail {
                                                ok = true;
                                                break;
                                            }
                                        }
                                        // Heuristic: in some forms, builder may reuse a shared const; best-effort only
                                    }
                                    _ => {}
                                }
                                j += 1;
                            }
                            if !ok {
                                eprintln!("[warn] dev verify: NewBox {} at v{} not followed by birth() call (expect {})", box_type, dst, expect_tail);
                                warn_count += 1;
                            }
                        }
                    }
                    idx += 1;
                }
            }
            if warn_count > 0 {
                eprintln!(
                    "[warn] dev verify: NewBox→birth invariant warnings: {}",
                    warn_count
                );
            }
        }

        module.add_function(function);

        // Dev stub: provide condition_fn when missing to satisfy predicate calls in JSON lexers
        // Returns integer 1 (truthy) and accepts one argument (unused).
        //
        // NOTE:
        // - MirFunction::new() はシグネチャの params に応じて
        //   [ValueId(0)..ValueId(param_count-1)] を事前に予約する。
        // - ここでは追加の next_value_id()/params.push() は行わず、
        //   予約済みのパラメータ集合をそのまま使う。
        if module.functions.get("condition_fn").is_none() {
            let sig = FunctionSignature {
                name: "condition_fn".to_string(),
                params: vec![MirType::Integer], // accept one i64-like arg
                return_type: MirType::Integer,
                effects: EffectMask::PURE,
            };
            let entry = BasicBlockId::new(0);
            let mut f = self.new_function_with_metadata(sig, entry);
            // body: const 1; return it（FunctionEmissionBox を使用）
            let one = crate::mir::function_emission::emit_const_integer(&mut f, entry, 1);
            crate::mir::function_emission::emit_return_value(&mut f, entry, one);
            module.add_function(f);
        }

        // main 関数スコープの Region スタックをポップするよ。
        crate::mir::region::observer::pop_function_region(self);

        // main 関数スコープの SlotRegistry を解放するよ。
        self.comp_ctx.current_slot_registry = None;

        // Phase 285LLVM-1.1: Copy user box declarations to module metadata for LLVM harness
        module.metadata.user_box_decls = self.comp_ctx.user_defined_boxes.clone();

        Ok(module)
    }

    fn annotate_missing_result_types_from_calls_and_await(
        &mut self,
        function: &super::MirFunction,
        module: &MirModule,
    ) {
        use crate::mir::definitions::Callee;
        use crate::mir::MirInstruction;

        for (_bid, bb) in function.blocks.iter() {
            for inst in bb.instructions.iter() {
                match inst {
                    MirInstruction::Await { dst, future } => {
                        if self.type_ctx.value_types.contains_key(dst) {
                            continue;
                        }
                        let inferred = match self.type_ctx.value_types.get(future) {
                            Some(MirType::Future(inner)) => (**inner).clone(),
                            _ => MirType::Unknown,
                        };
                        self.type_ctx.value_types.insert(*dst, inferred);
                    }
                    MirInstruction::Call {
                        dst: Some(dst),
                        callee: Some(callee),
                        ..
                    } => {
                        if self.type_ctx.value_types.contains_key(dst) {
                            continue;
                        }
                        let inferred = match callee {
                            Callee::Global(name) => module
                                .functions
                                .get(name)
                                .map(|f| f.signature.return_type.clone())
                                .or_else(|| {
                                    crate::mir::builder::types::annotation::annotate_from_function(
                                        self, *dst, name,
                                    );
                                    self.type_ctx.value_types.get(dst).cloned()
                                })
                                .unwrap_or(MirType::Unknown),
                            Callee::Constructor { box_type } => {
                                let ret = MirType::Box(box_type.clone());
                                self.type_ctx
                                    .value_origin_newbox
                                    .insert(*dst, box_type.clone());
                                ret
                            }
                            _ => MirType::Unknown,
                        };
                        self.type_ctx.value_types.insert(*dst, inferred);
                    }
                    MirInstruction::ExternCall { dst: Some(dst), .. }
                    | MirInstruction::BoxCall { dst: Some(dst), .. }
                    | MirInstruction::PluginInvoke { dst: Some(dst), .. } => {
                        if !self.type_ctx.value_types.contains_key(dst) {
                            self.type_ctx.value_types.insert(*dst, MirType::Unknown);
                        }
                    }
                    _ => {}
                }
            }
        }
    }

    // Phase 131-11-E: Re-propagate BinOp result types after PHI resolution
    // This fixes cases where BinOp instructions were created before PHI types were known
    // Phase 279 P0: repropagate_binop_types() method removed
    // Moved to TypePropagationPipeline (SSOT)
}

// Phase 279 P0: OperandTypeClass enum removed
// Moved to TypePropagationPipeline (SSOT)
