//! Pattern Router - Table-driven dispatch for loop patterns
//!
//! Phase 194: Replace if/else chain with table-driven routing
//!
//! # Architecture
//!
//! - Each pattern registers a detect function and a lower function
//! - Patterns are tried in priority order (lower = tried first)
//! - First matching pattern wins
//! - Feature extraction delegated to ast_feature_extractor module
//!
//! # Adding New Patterns
//!
//! 1. Create a new module in `patterns/` (e.g., `pattern4_your_name.rs`)
//! 2. Implement `pub fn can_lower(ctx: &LoopPatternContext) -> bool`
//! 3. Implement `pub fn lower(builder: &mut MirBuilder, ctx: &LoopPatternContext) -> Result<Option<ValueId>, String>`
//! 4. Add entry to `LOOP_PATTERNS` table below
//!
//! That's it! No need to modify routing logic.

use crate::ast::ASTNode;
use crate::mir::builder::MirBuilder;
use crate::mir::ValueId;

use crate::mir::loop_pattern_detection::{LoopFeatures, LoopPatternKind};

// Phase 273 P1: Import Plan components (DomainPlan → Normalizer → Verifier → Lowerer)
use crate::mir::builder::control_flow::plan::lowerer::PlanLowerer;
use crate::mir::builder::control_flow::plan::normalizer::PlanNormalizer;
use crate::mir::builder::control_flow::plan::verifier::PlanVerifier;
use crate::mir::builder::control_flow::plan::single_planner;

/// AST Feature Extractor (declared in mod.rs as pub module, import from parent)
use super::ast_feature_extractor as ast_features;

/// Phase 92 P0-2: Import LoopSkeleton for Option A
use crate::mir::loop_canonicalizer::LoopSkeleton;

/// Context passed to pattern detect/lower functions
pub(crate) struct LoopPatternContext<'a> {
    /// Loop condition AST node
    pub condition: &'a ASTNode,

    /// Loop body statements
    pub body: &'a [ASTNode],

    /// Current function name (for routing)
    pub func_name: &'a str,

    /// Debug logging enabled
    pub debug: bool,

    /// In static box context? (affects Pattern8 routing)
    pub in_static_box: bool,

    /// Has continue statement(s) in body? (Phase 194+)
    #[allow(dead_code)]
    pub has_continue: bool,

    /// Has break statement(s) in body? (Phase 194+)
    #[allow(dead_code)]
    pub has_break: bool,

    /// Phase 192: Loop features extracted from AST
    #[allow(dead_code)]
    pub features: LoopFeatures,

    /// Phase 192: Pattern classification based on features
    pub pattern_kind: LoopPatternKind,

    /// Phase 200-C: Optional function body AST for capture analysis
    /// None if not available, Some(&[ASTNode]) if function body is accessible
    pub fn_body: Option<&'a [ASTNode]>,

    /// Phase 92 P0-2: Optional LoopSkeleton from canonicalizer
    /// This provides ConditionalStep information for Pattern2 lowering.
    /// None if canonicalizer hasn't run yet (backward compatibility).
    /// SSOT Principle: Avoid re-detecting ConditionalStep in lowering phase.
    #[allow(dead_code)]
    pub skeleton: Option<&'a LoopSkeleton>,

    /// Phase 188.3: Cached StepTree max_loop_depth for Pattern6
    /// None if not computed, Some(depth) if Pattern6 candidate
    /// Avoids re-building StepTree in lowering phase
    pub step_tree_max_loop_depth: Option<u32>,
}

impl<'a> LoopPatternContext<'a> {
    /// Create new context from routing parameters
    ///
    /// Automatically detects continue/break statements in body
    /// Extracts features and classifies pattern from AST
    /// Detects infinite loop condition
    /// Uses choose_pattern_kind() SSOT entry point
    pub(crate) fn new(
        condition: &'a ASTNode,
        body: &'a [ASTNode],
        func_name: &'a str,
        debug: bool,
        in_static_box: bool,
    ) -> Self {
        // Use AST Feature Extractor for break/continue detection
        let has_continue = ast_features::detect_continue_in_body(body);
        let has_break = ast_features::detect_break_in_body(body);

        // Extract features (includes infinite loop detection)
        let features = ast_features::extract_features(condition, body, has_continue, has_break);

        // Phase 137-6-S1: Use SSOT pattern selection entry point
        use crate::mir::builder::control_flow::joinir::routing::choose_pattern_kind;
        let pattern_kind = choose_pattern_kind(condition, body);

        Self {
            condition,
            body,
            func_name,
            debug,
            in_static_box,
            has_continue,
            has_break,
            features,
            pattern_kind,
            fn_body: None, // Phase 200-C: Default to None
            skeleton: None, // Phase 92 P0-2: Default to None
            step_tree_max_loop_depth: None, // Phase 188.3: Default to None
        }
    }

    /// Phase 200-C: Create context with fn_body for capture analysis
    pub(crate) fn with_fn_body(
        condition: &'a ASTNode,
        body: &'a [ASTNode],
        func_name: &'a str,
        debug: bool,
        in_static_box: bool,
        fn_body: &'a [ASTNode],
    ) -> Self {
        let mut ctx = Self::new(condition, body, func_name, debug, in_static_box);
        ctx.fn_body = Some(fn_body);
        ctx
    }

    /// Phase 92 P0-2: Set skeleton (for canonicalizer integration)
    #[allow(dead_code)]
    pub(crate) fn with_skeleton(mut self, skeleton: &'a LoopSkeleton) -> Self {
        self.skeleton = Some(skeleton);
        self
    }
}

/// Phase 286 P2.2: Common helper for Plan line lowering
///
/// Extracts the common 3-line pattern used by Pattern6/7/4/1:
/// 1. Normalize DomainPlan → CorePlan
/// 2. Verify CorePlan invariants (fail-fast)
/// 3. Lower CorePlan → MIR
///
/// This eliminates repetition across 4 pattern routing blocks.
fn lower_via_plan(
    builder: &mut MirBuilder,
    domain_plan: crate::mir::builder::control_flow::plan::DomainPlan,
    ctx: &LoopPatternContext,
) -> Result<Option<ValueId>, String> {
    let core_plan = PlanNormalizer::normalize(builder, domain_plan, ctx)?;
    PlanVerifier::verify(&core_plan)?;
    PlanLowerer::lower(builder, core_plan, ctx)
}

// Phase 29ai P5: Plan extractor routing moved to `plan::single_planner`.

/// Phase 272 P0.2 Refactoring: can_lower() strategy classification
///
/// Clarifies the two main detection strategies used across patterns:
///
/// ## ExtractionBased (SSOT Approach)
/// - Used by: Pattern6, Pattern7
/// - Strategy: Try pattern extraction, if successful → match
/// - Pros: Single source of truth (extract function defines pattern)
/// - Cons: Extraction can be expensive (but amortized over lowering)
///
/// ## StructureBased (Feature Classification)
/// - Used by: Pattern1, Pattern2, Pattern3, Pattern4, Pattern5, Pattern8, Pattern9
/// - Strategy: Check pattern_kind (from LoopPatternContext), plus optional structural checks
/// - Pros: Fast classification, reuses centralized feature detection
/// - Cons: Two sources of truth (classify + structural checks)
///
/// ## Rationale for Dual Strategy:
/// - Pattern6/7: Complex extraction logic (variable step, carrier tracking)
///   → ExtractionBased avoids duplication between detect and extract
/// - Other patterns: Simple structural features (break/continue/if-phi)
///   → StructureBased leverages centralized LoopFeatures classification
///
/// This documentation prevents bugs like Phase 272 P0.2's Pattern7 issue
/// (pattern_kind check was too restrictive, extraction-based approach fixed it).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(dead_code)]  // Documentation purpose - not enforced in code yet
pub(crate) enum CanLowerStrategy {
    /// Extraction-based detection: Try extract(), success → match
    /// Used by Pattern6, Pattern7
    ExtractionBased,

    /// Structure-based detection: Check pattern_kind from LoopPatternContext
    /// Used by Pattern1, Pattern2, Pattern3, Pattern4, Pattern5, Pattern8, Pattern9
    StructureBased,
}

/// Entry in the loop pattern router table.
/// Each pattern registers a detect function and a lower function.
pub(crate) struct LoopPatternEntry {
    /// Human-readable pattern name for debugging
    pub(crate) name: &'static str,

    /// Detection function: returns true if this pattern matches
    pub(crate) detect: fn(&MirBuilder, &LoopPatternContext) -> bool,

    /// Lowering function: performs the actual JoinIR generation
    pub(crate) lower: fn(&mut MirBuilder, &LoopPatternContext) -> Result<Option<ValueId>, String>,
}

/// Static table of all registered loop patterns.
///
/// **IMPORTANT**: Patterns are tried in array order (SSOT).
/// Array order defines priority - earlier entries are tried first.
/// Pattern4 → Pattern8 → Pattern9 → Pattern3 → Pattern1 → Pattern2
///
/// # Current Patterns (Structure-based detection, established Phase 131-11+)
///
/// Pattern detection strategies (updated Phase 286):
/// - Pattern6/7: ExtractionBased (Plan line, Phase 273+)
/// - Pattern5/8/9: ExtractionBased (extraction functions, Plan line Phase 286+)
/// - Pattern1-4: StructureBased (LoopFeatures classification, legacy)
///
/// - Pattern 4: Loop with Continue (loop_continue_pattern4.hako)
///   - Detection: pattern_kind == Pattern4Continue
///   - Structure: has_continue && !has_break
///
/// - Pattern 3: Loop with If-Else PHI (loop_if_phi.hako)
///   - Detection: pattern_kind == Pattern3IfPhi
///   - Structure: has_if_else_phi && !has_break && !has_continue
///
/// - Pattern 1: Simple While Loop (loop_min_while.hako)
///   - Detection: pattern_kind == Pattern1SimpleWhile
///   - Structure: !has_break && !has_continue && !has_if_else_phi
///
/// - Pattern 2: Loop with Conditional Break (joinir_min_loop.hako)
///   - Detection: pattern_kind == Pattern2Break
///   - Structure: has_break && !has_continue
///
/// Note: func_name is now only used for debug logging, not pattern detection
/// Phase 286: Pattern5 removed (migrated to Plan-based routing)
pub(crate) static LOOP_PATTERNS: &[LoopPatternEntry] = &[
    LoopPatternEntry {
        name: "Pattern6_NestedLoopMinimal",  // Phase 188.3: 1-level nested loop
        detect: super::pattern6_nested_minimal::can_lower,
        lower: super::pattern6_nested_minimal::lower,
    },
    LoopPatternEntry {
        name: "Pattern4_WithContinue",
        detect: super::pattern4_with_continue::can_lower,
        lower: super::pattern4_with_continue::lower,
    },
    // Phase 273 P0.1: Pattern6 entry removed (migrated to Plan-based routing)
    // Pattern6_ScanWithInit now handled via extract_scan_with_init_plan() + PlanLowerer
    // Phase 273 P2: Pattern7 entry removed (migrated to Plan-based routing)
    // Pattern7_SplitScan now handled via extract_split_scan_plan() + PlanLowerer
    LoopPatternEntry {
        name: "Pattern8_BoolPredicateScan", // Phase 259 P0: boolean predicate scan (is_integer/is_valid)
        detect: super::pattern8_scan_bool_predicate::can_lower,
        lower: super::pattern8_scan_bool_predicate::lower,
    },
    LoopPatternEntry {
        name: "Pattern9_AccumConstLoop", // Phase 270 P1: accumulator const loop (橋渡しパターン, before P1)
        detect: super::pattern9_accum_const_loop::can_lower,
        lower: super::pattern9_accum_const_loop::lower,
    },
    LoopPatternEntry {
        name: "Pattern3_WithIfPhi",
        detect: super::pattern3_with_if_phi::can_lower,
        lower: super::pattern3_with_if_phi::lower,
    },
    LoopPatternEntry {
        name: "Pattern1_Minimal",
        detect: super::pattern1_minimal::can_lower,
        lower: super::pattern1_minimal::lower,
    },
    LoopPatternEntry {
        name: "Pattern2_WithBreak",
        detect: super::pattern2_with_break::can_lower,
        lower: super::pattern2_with_break::lower,
    },
];

/// Try all registered patterns in priority order.
///
/// Returns Ok(Some(value_id)) if a pattern matched and lowered successfully.
/// Returns Ok(None) if no pattern matched.
/// Returns Err if a pattern matched but lowering failed.
///
/// # Router Architecture (Structure-based routing established Phase 183)
///
/// This router uses multiple detection strategies:
/// - Plan-based (Pattern6/7): extract_*_plan() → DomainPlan (Phase 273+ SSOT)
/// - Extraction-based (Pattern8/9): extract_*() functions (already implemented)
/// - Structure-based (Pattern1-5): ctx.pattern_kind classification (legacy)
///
/// # Plan Line SSOT for Pattern6/7 (Phase 273+)
///
/// This function implements the following routing strategy:
/// 1. Try Plan-based Pattern6 (extract_scan_with_init_plan) → DomainPlan
/// 2. Try Plan-based Pattern7 (extract_split_scan_plan) → DomainPlan
/// 3. Fall through to legacy Pattern1-5 table for other patterns
///
/// The Plan line (Extractor → Normalizer → Verifier → Lowerer) is the
/// current operational SSOT for Pattern6/7. Legacy patterns (1-5) use
/// the traditional LoopPatternContext-based routing.
///
/// Plan-based architecture (Phase 273 P1-P3):
/// - extract_*_plan() → DomainPlan (pure extraction, no builder)
/// - PlanNormalizer::normalize() → CorePlan (pattern knowledge expansion, SSOT)
/// - PlanVerifier::verify() → fail-fast validation
/// - PlanLowerer::lower() → MIR emission (pattern-agnostic, emit_frag SSOT)
///
/// SSOT Entry Points:
/// - Pattern6: src/mir/builder/control_flow/plan/normalizer.rs (ScanWithInit normalization)
/// - Pattern7: src/mir/builder/control_flow/plan/normalizer.rs (SplitScan normalization)
/// - Pattern1-5: src/mir/builder/control_flow/joinir/patterns/pattern*.rs (direct lowering)
pub(crate) fn route_loop_pattern(
    builder: &mut MirBuilder,
    ctx: &LoopPatternContext,
) -> Result<Option<ValueId>, String> {
    use super::super::trace;

    // Phase 29ai P5: Single entrypoint for plan extraction (router has no rule table).
    if let Some(domain_plan) = single_planner::try_build_domain_plan(ctx)? {
        return lower_via_plan(builder, domain_plan, ctx);
    }

    // Phase 183: Route based on pre-classified pattern kind
    // Pattern kind was already determined by ctx.pattern_kind in LoopPatternContext::new()
    // This eliminates duplicate detection logic across routers.

    // Find matching pattern entry based on pattern_kind
    // Phase 273 P0.1: Pattern6 skip logic removed (entry no longer in LOOP_PATTERNS)
    for entry in LOOP_PATTERNS {
        if (entry.detect)(builder, ctx) {
            let log_msg = format!("route=joinir strategy=extract pattern={} (Phase 194+)", entry.name);
            trace::trace().pattern("route", &log_msg, true);
            return (entry.lower)(builder, ctx);
        }
    }

    // No pattern matched - return None (caller will handle error)
    if ctx.debug {
        trace::trace().debug(
            "route",
            &format!(
                "route=none (no pattern matched) func='{}' pattern_kind={:?} (exhausted: plan+joinir)",
                ctx.func_name, ctx.pattern_kind
            ),
        );
    }
    Ok(None)
}
