//! Phase 29ai P5: Rule list (order SSOT) + guards
//!
//! IMPORTANT: Keep rule order identical to the legacy `PLAN_EXTRACTORS` table
//! (observability/behavior must not change).

use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;

use crate::mir::builder::control_flow::plan::extractors;
use crate::mir::builder::control_flow::plan::facts::pattern2_loopbodylocal_facts::LoopBodyLocalShape;
use crate::mir::builder::control_flow::plan::planner::{self, PlannerContext};
use crate::mir::builder::control_flow::plan::DomainPlan;

use super::rule_order::{rule_name, PlanRuleId, PLAN_RULE_ORDER};

pub(super) fn try_build_domain_plan(ctx: &LoopPatternContext) -> Result<Option<DomainPlan>, String> {
    use crate::mir::builder::control_flow::joinir::trace;

    let planner_ctx = PlannerContext {
        pattern_kind: Some(ctx.pattern_kind),
        in_static_box: ctx.in_static_box,
        debug: ctx.debug,
    };
    let outcome = planner::build_plan_with_facts_ctx(&planner_ctx, ctx.condition, ctx.body)
        .map_err(|freeze| freeze.to_string())?;
    let planner_opt = outcome.plan.clone();
    let promotion_facts = outcome
        .facts
        .as_ref()
        .and_then(|facts| facts.facts.pattern2_loopbodylocal.as_ref());

    for rule_id in PLAN_RULE_ORDER {
        let rule_id = *rule_id;
        let name = rule_name(rule_id);
        let planner_hit = try_take_planner(&planner_opt, rule_id);
        let allow_pattern8 = !ctx.in_static_box;
        let (plan_opt, log_none) = if planner_hit.is_some() {
            (planner_hit, false)
        } else {
            (fallback_extract(ctx, rule_id, allow_pattern8)?, true)
        };

        let promotion_tag = if matches!(rule_id, PlanRuleId::Pattern2)
            && crate::config::env::joinir_dev::strict_enabled()
        {
            promotion_facts.map(|facts| match facts.shape {
                LoopBodyLocalShape::TrimSeg { .. } => "[plan/pattern2/promotion_hint:TrimSeg]",
                LoopBodyLocalShape::DigitPos { .. } => "[plan/pattern2/promotion_hint:DigitPos]",
            })
        } else {
            None
        };

        if let Some(tag) = promotion_tag {
            eprintln!("{}", tag);
        }

        if let Some(domain_plan) = plan_opt {
            let log_msg = format!("route=plan strategy=extract pattern={}", name);
            trace::trace().pattern("route", &log_msg, true);
            return Ok(Some(domain_plan));
        } else if log_none && ctx.debug {
            let debug_msg = format!("{} extraction returned None, trying next pattern", name);
            trace::trace().debug("route", &debug_msg);
        }
    }

    Ok(None)
}

fn try_take_planner(planner_opt: &Option<DomainPlan>, kind: PlanRuleId) -> Option<DomainPlan> {
    match (kind, planner_opt.as_ref()) {
        (PlanRuleId::Pattern1, Some(DomainPlan::Pattern1SimpleWhile(_))) => planner_opt.clone(),
        (PlanRuleId::Pattern2, Some(DomainPlan::Pattern2Break(_))) => planner_opt.clone(),
        (PlanRuleId::Pattern3, Some(DomainPlan::Pattern3IfPhi(_))) => planner_opt.clone(),
        (PlanRuleId::Pattern4, Some(DomainPlan::Pattern4Continue(_))) => planner_opt.clone(),
        (PlanRuleId::Pattern5, Some(DomainPlan::Pattern5InfiniteEarlyExit(_))) => {
            planner_opt.clone()
        }
        (PlanRuleId::Pattern6, Some(DomainPlan::ScanWithInit(_))) => planner_opt.clone(),
        (PlanRuleId::Pattern7, Some(DomainPlan::SplitScan(_))) => planner_opt.clone(),
        (PlanRuleId::Pattern8, Some(DomainPlan::Pattern8BoolPredicateScan(_))) => {
            planner_opt.clone()
        }
        (PlanRuleId::Pattern9, Some(DomainPlan::Pattern9AccumConstLoop(_))) => {
            planner_opt.clone()
        }
        _ => None,
    }
}

fn fallback_extract(
    ctx: &LoopPatternContext,
    kind: PlanRuleId,
    allow_pattern8: bool,
) -> Result<Option<DomainPlan>, String> {
    match kind {
        PlanRuleId::Pattern1 => extractors::pattern1::extract_pattern1_plan(ctx.condition, ctx.body),
        PlanRuleId::Pattern2 => {
            extractors::pattern2_break::extract_pattern2_plan(ctx.condition, ctx.body)
        }
        PlanRuleId::Pattern3 => extractors::pattern3::extract_pattern3_plan(ctx.condition, ctx.body),
        PlanRuleId::Pattern4 => extractors::pattern4::extract_pattern4_plan(ctx.condition, ctx.body),
        PlanRuleId::Pattern5 => extractors::pattern5::extract_pattern5_plan(ctx.condition, ctx.body),
        PlanRuleId::Pattern6 => {
            extractors::pattern6_scan_with_init::extract_scan_with_init_plan(
                ctx.condition,
                ctx.body,
                ctx.fn_body,
            )
        }
        PlanRuleId::Pattern7 => extractors::pattern7_split_scan::extract_split_scan_plan(
            ctx.condition,
            ctx.body,
            &[],
        ),
        PlanRuleId::Pattern8 => {
            if !allow_pattern8 {
                return Ok(None);
            }
            extractors::pattern8::extract_pattern8_plan(ctx.condition, ctx.body)
        }
        PlanRuleId::Pattern9 => extractors::pattern9::extract_pattern9_plan(ctx.condition, ctx.body),
    }
}
