//! Ordered search-intent rules keyed on normalized query phrases.
//!
//! Each rule contributes goals, framework hints, or witness flags when its phrase predicates match
//! the query context built by `intent/context.rs`.

use crate::domain::{ArtifactBias, PlannerStrictness, SearchGoal, SearchIntentRuleId};

use super::{QueryContext, SearchIntentBuilder};

#[derive(Debug, Clone, Copy)]
pub(super) struct SearchIntentRule {
    pub(super) id: SearchIntentRuleId,
    pub(super) apply: fn(&QueryContext, &mut SearchIntentBuilder) -> bool,
}

fn apply_docs_terms(context: &QueryContext, builder: &mut SearchIntentBuilder) -> bool {
    if !context.has_any(&[
        "docs",
        "documented",
        "documentation",
        "public docs",
        "contract",
        "contracts",
        "readme",
        "invalid_params",
        "error_code",
        "typed error",
        "citation",
        "citations",
    ]) {
        return false;
    }

    builder.insert_goal(SearchGoal::Documentation);
    true
}

fn apply_tests_terms(context: &QueryContext, builder: &mut SearchIntentBuilder) -> bool {
    if !context.has_any(&[
        "test",
        "tests",
        "coverage",
        "assert",
        "parity",
        "canary",
        "replay",
        "conformance",
        "inspector",
    ]) {
        return false;
    }

    builder.insert_goal(SearchGoal::Tests);
    true
}

fn apply_examples_terms(context: &QueryContext, builder: &mut SearchIntentBuilder) -> bool {
    if !context.has_any(&[
        "example",
        "examples",
        "quickstart",
        "getting started",
        "getting-started",
        "setup",
        "install",
    ]) {
        return false;
    }

    builder.insert_goal(SearchGoal::Examples);
    true
}

fn apply_onboarding_terms(context: &QueryContext, builder: &mut SearchIntentBuilder) -> bool {
    if !context.has_any(&[
        "quickstart",
        "getting started",
        "getting-started",
        "setup",
        "configure",
        "configuring",
        "install",
        "installation",
    ]) {
        return false;
    }

    builder.insert_goal(SearchGoal::Onboarding);
    true
}

fn apply_readme_terms(context: &QueryContext, builder: &mut SearchIntentBuilder) -> bool {
    if !builder.has_goal(SearchGoal::Onboarding) && !context.has_any(&["readme", "documented"]) {
        return false;
    }

    builder.insert_goal(SearchGoal::Readme);
    true
}

fn apply_contract_terms(context: &QueryContext, builder: &mut SearchIntentBuilder) -> bool {
    if !context.has_any(&[
        "contract",
        "contracts",
        "invalid_params",
        "error_code",
        "typed error",
        "unavailable",
        "strict_failure",
    ]) {
        return false;
    }

    builder.insert_goal(SearchGoal::Contracts);
    true
}

fn apply_error_taxonomy_terms(context: &QueryContext, builder: &mut SearchIntentBuilder) -> bool {
    if !context.has_any(&[
        "invalid_params",
        "-32602",
        "error taxonomy",
        "unavailable",
        "strict_failure",
        "semantic_status",
        "semantic_reason",
    ]) {
        return false;
    }

    builder.insert_goal(SearchGoal::ErrorTaxonomy);
    true
}

fn apply_tool_contract_terms(context: &QueryContext, builder: &mut SearchIntentBuilder) -> bool {
    if !(context.has_any(&[
        "search_hybrid",
        "semantic_status",
        "semantic_reason",
        "tool schema",
        "tool contract",
        "tool contracts",
        "tool surface",
        "tools/list",
        "mcp tool",
        "mcp tools",
        "core versus extended",
        "core vs extended",
        "extended_only",
    ]) || (context.has_any(&["mcp", "tool", "tools"])
        && context.has_any(&["core", "extended", "schema"])))
    {
        return false;
    }

    builder.insert_goal(SearchGoal::ToolContracts);
    true
}

fn apply_mcp_runtime_surface_terms(
    context: &QueryContext,
    builder: &mut SearchIntentBuilder,
) -> bool {
    if !(context.has_any(&[
        "mcp http",
        "http startup",
        "loopback http",
        "workspace_attach",
        "tool surface",
        "tools/list",
        "core versus extended",
        "core vs extended",
        "extended_only",
    ]) || (context.has_any(&["mcp"])
        && context.has_any(&[
            "http", "startup", "runtime", "tool", "tools", "surface", "core", "extended", "attach",
            "loopback",
        ])))
    {
        return false;
    }

    builder.insert_goal(SearchGoal::McpRuntimeSurface);
    true
}

fn apply_fixtures_terms(context: &QueryContext, builder: &mut SearchIntentBuilder) -> bool {
    if !context.has_any(&[
        "fixture",
        "fixtures",
        "playbook",
        "playbooks",
        "replay",
        "trace artifact",
    ]) {
        return false;
    }

    builder.insert_goal(SearchGoal::Fixtures);
    true
}

fn apply_benchmarks_terms(context: &QueryContext, builder: &mut SearchIntentBuilder) -> bool {
    if !(context.has_any(&[
        "benchmark",
        "benchmarks",
        "metric",
        "metrics",
        "acceptance metric",
        "acceptance metrics",
        "replayability",
        "deterministic replay",
    ]) || (context.has_any(&["deterministic", "replay", "suite", "fixture", "fixtures"])
        && context.has_any(&["trace artifact", "citation", "citations", "playbook"])))
    {
        return false;
    }

    builder.insert_goal(SearchGoal::Benchmarks);
    true
}

fn apply_runtime_config_artifact_terms(
    context: &QueryContext,
    builder: &mut SearchIntentBuilder,
) -> bool {
    if !(context.is_runtime_config_shorthand()
        || (context.has_any(&["config", "configuration"])
            && (context.has_any_token(&["package"])
                || context.has_any(&["workspace", "workspaces", "build", "builds"])))
        || (context.has_any(&["config", "configuration"])
            && context.has_any(&[
                "workflow",
                "workflows",
                "github workflow",
                "github action",
                "github actions",
                "gh pages",
            ]))
        || context.has_any(&[
            "runtime config",
            "pyproject",
            "setup.py",
            "cargo.toml",
            "package.json",
            "package-lock.json",
            "pnpm-lock.yaml",
            "yarn.lock",
            "composer.json",
            "composer.lock",
            "tsconfig.json",
            "go.mod",
            "go.sum",
            "requirements.txt",
            "pipfile",
            "mix.exs",
        ])
        || (context.has_manifest_hint()
            && context.has_any(&[
                "config",
                "manifest",
                "manifests",
                "dependency",
                "dependencies",
                "lock",
                "workspace",
                "workspaces",
                "build",
                "builds",
            ])))
    {
        return false;
    }

    builder.insert_artifact_bias(ArtifactBias::RuntimeConfigArtifact);
    true
}

fn apply_laravel_ui_witness_terms(
    context: &QueryContext,
    builder: &mut SearchIntentBuilder,
) -> bool {
    if !context.mentions_laravel_ui() {
        return false;
    }

    builder.insert_artifact_bias(ArtifactBias::LaravelUi);
    true
}

fn apply_laravel_form_action_witness_terms(
    context: &QueryContext,
    builder: &mut SearchIntentBuilder,
) -> bool {
    if !builder.has_artifact_bias(ArtifactBias::LaravelUi) || !context.has_blade_form_action_terms()
    {
        return false;
    }

    builder.insert_artifact_bias(ArtifactBias::LaravelFormAction);
    true
}

fn apply_blade_component_witness_terms(
    context: &QueryContext,
    builder: &mut SearchIntentBuilder,
) -> bool {
    if !(context.has_any(&["blade"])
        && ((context.has_any(&["component", "components"])
            && context.has_any(&["layout", "slot", "section", "render", "view", "views"]))
            || context.has_blade_form_action_terms())
        && !context.has_any(&["livewire", "flux"]))
    {
        return false;
    }

    builder.insert_artifact_bias(ArtifactBias::BladeComponent);
    true
}

fn apply_livewire_view_witness_terms(
    context: &QueryContext,
    builder: &mut SearchIntentBuilder,
) -> bool {
    if !builder.has_artifact_bias(ArtifactBias::LaravelUi)
        || !context.has_any(&[
            "app livewire",
            "wire:model",
            "wire:click",
            "render",
            "class",
        ])
    {
        return false;
    }

    builder.insert_artifact_bias(ArtifactBias::LivewireView);
    true
}

fn apply_laravel_layout_witness_terms(
    context: &QueryContext,
    builder: &mut SearchIntentBuilder,
) -> bool {
    if !builder.has_artifact_bias(ArtifactBias::LaravelUi)
        || !context.has_any(&["layout", "layouts"])
        || !context.has_any(&["navigation", "page", "pages", "header", "footer", "sidebar"])
    {
        return false;
    }

    builder.insert_artifact_bias(ArtifactBias::LaravelLayout);
    true
}

fn apply_commands_middleware_witness_terms(
    context: &QueryContext,
    builder: &mut SearchIntentBuilder,
) -> bool {
    if !context.has_any(&["command", "commands", "console", "middleware", "artisan"]) {
        return false;
    }

    builder.insert_artifact_bias(ArtifactBias::CommandsMiddleware);
    true
}

fn apply_jobs_listeners_witness_terms(
    context: &QueryContext,
    builder: &mut SearchIntentBuilder,
) -> bool {
    if !context.has_any(&[
        "job",
        "jobs",
        "listener",
        "listeners",
        "event",
        "events",
        "queue",
    ]) {
        return false;
    }

    builder.insert_artifact_bias(ArtifactBias::JobsListeners);
    true
}

fn apply_runtime_witness_terms(context: &QueryContext, builder: &mut SearchIntentBuilder) -> bool {
    let explicit_runtime_signal = context.has_any(&[
        "initialize",
        "initialization",
        "server capabilities",
        "initialize result",
        "subscription",
        "subscriptions",
        "completion provider",
        "completion providers",
        "handler",
        "handlers",
        "transport",
        "notification",
        "notifications",
        "resource updated",
        "resource_updated",
        "custom method",
        "auth token",
        "http auth",
        "bearer auth",
        "authorization",
        "entrypoint",
        "client communication",
        "back to client",
        "client aware",
        "client-aware",
        "client gateway",
        "clientgateway",
        "conformance",
        "inspector",
        "resource update",
        "entry point",
        "entry-point",
        "app startup",
        "startup module",
        "cli main",
        "main module",
        "runtime config",
    ]);
    let generic_runtime_signal = context.has_any_token(&["runtime"])
        && (context.has_any(&[
            "self hosted",
            "self-hosted",
            "edge function",
            "edge functions",
            "api",
            "server",
            "service",
            "services",
            "worker",
            "workers",
            "handler",
            "handlers",
            "router",
            "route",
            "routes",
            "transport",
            "http",
            "docker",
            "wasm",
            "cli",
            "bootstrap",
            "startup",
            "entrypoint",
            "entry point",
            "entry-point",
        ]) || (context.has_any_token(&["function", "functions"])
            && context.has_any(&["edge", "self hosted", "self-hosted", "api", "runtime"])));
    let ui_runtime_signal = context.has_ui_runtime_surface_terms()
        && (context.has_any(&[
            "android",
            "compose",
            "playwright",
            "react",
            "svelte",
            "tsx",
            "viewmodel",
            "vue",
        ]) || context.has_any_token(&["runtime"]));
    let backend_runtime_signal = context.has_backend_runtime_surface_terms()
        && (context.mentions_supported_language_family()
            || context.has_strong_test_focus_terms()
            || context.has_runtime_supporting_terms());
    if !(explicit_runtime_signal
        || generic_runtime_signal
        || ui_runtime_signal
        || backend_runtime_signal
        || context.mentions_model_data_surface()
        || builder.has_artifact_bias(ArtifactBias::LaravelUi)
        || builder.has_goal(SearchGoal::EntryPointBuildFlow)
        || builder.has_goal(SearchGoal::NavigationFallbacks))
    {
        return false;
    }

    builder.insert_goal(SearchGoal::RuntimeWitnesses);
    true
}

fn apply_entrypoint_build_flow_terms(
    context: &QueryContext,
    builder: &mut SearchIntentBuilder,
) -> bool {
    if !(context.has_any(&[
        "where the app starts",
        "app starts",
        "startup flow",
        "app startup",
        "entry point",
        "entry-point",
        "startup module",
        "cli main",
        "main module",
        "start and build",
        "starts and builds",
        "build pipeline",
        "pipeline runner",
    ]) || (context.has_any(&["entrypoint", "entry point", "entry-point"])
        && context.has_any(&["cli", "command", "commands", "bin"]))
        || (context.has_any(&[
            "start",
            "starts",
            "startup",
            "entrypoint",
            "boot",
            "bootstrap",
        ]) && context.has_any(&[
            "build",
            "builds",
            "builder",
            "construct",
            "constructs",
            "wire",
            "wires",
            "wiring",
            "runner",
            "pipeline",
        ]))
        || (context.has_any(&["route", "routes", "middleware", "provider", "providers"])
            && context.has_any(&["bootstrap", "entrypoint", "entry point", "app"])))
    {
        return false;
    }

    builder.insert_goal(SearchGoal::EntryPointBuildFlow);
    true
}

fn apply_ci_workflow_witness_terms(
    context: &QueryContext,
    builder: &mut SearchIntentBuilder,
) -> bool {
    let explicit_ci_workflow_signal = context.has_any(&[
        "build pipeline",
        "github workflow",
        "github action",
        "github actions",
        "autofix",
        "release workflow",
        "publish workflow",
        "deploy workflow",
    ]);
    let generic_workflow_signal = context.has_any(&["workflow", "workflows"])
        && context.has_any(&[
            "github", "ci", "action", "actions", "yaml", "yml", "release", "publish", "deploy",
            "autofix", "build",
        ]);
    if !(explicit_ci_workflow_signal || generic_workflow_signal) {
        return false;
    }

    builder.insert_artifact_bias(ArtifactBias::CiWorkflow);
    true
}

fn apply_scripts_ops_witness_terms(
    context: &QueryContext,
    builder: &mut SearchIntentBuilder,
) -> bool {
    if !(context.has_any_token(&["script", "scripts"])
        || context.has_any(&["justfile", "makefile", "xtask", "changelog"]))
    {
        return false;
    }

    builder.insert_artifact_bias(ArtifactBias::ScriptsOps);
    true
}

fn apply_navigation_fallback_terms(
    context: &QueryContext,
    builder: &mut SearchIntentBuilder,
) -> bool {
    if !context.has_any(&[
        "find_implementations",
        "find implementations",
        "go_to_definition",
        "go to definition",
        "find_references",
        "find references",
        "incoming_calls",
        "incoming calls",
        "outgoing_calls",
        "outgoing calls",
        "precise navigation",
        "precise data",
        "precise_absent",
        "precise navigation data",
        "fallback",
        "navigation",
        "scip",
    ]) {
        return false;
    }

    builder.insert_goal(SearchGoal::NavigationFallbacks);
    true
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum TestWitnessFocusMode {
    WitnessFocused,
    RuntimeBridge,
}

fn apply_test_witness_focus(context: &QueryContext, builder: &mut SearchIntentBuilder) -> bool {
    let Some(mode) = test_witness_focus_mode(context, builder) else {
        return false;
    };

    builder.insert_artifact_bias(ArtifactBias::TestWitness);
    match mode {
        TestWitnessFocusMode::WitnessFocused => {
            builder.strictness = Some(PlannerStrictness::WitnessFocused);
        }
        TestWitnessFocusMode::RuntimeBridge => {
            builder.insert_goal(SearchGoal::RuntimeWitnesses);
            builder.strictness = Some(PlannerStrictness::Broad);
        }
    }

    true
}

fn test_witness_focus_mode(
    context: &QueryContext,
    builder: &SearchIntentBuilder,
) -> Option<TestWitnessFocusMode> {
    if should_skip_test_witness_focus(context, builder) || !builder.has_goal(SearchGoal::Tests) {
        return None;
    }

    if is_runtime_bridge_test_focus(context, builder) {
        return Some(TestWitnessFocusMode::RuntimeBridge);
    }

    let allow_readme = context.has_strong_test_focus_terms();
    if is_laravel_ui_path_hint_test_focus(builder)
        || is_docs_with_strong_test_focus(context, builder, allow_readme)
        || is_plain_test_focus(builder, allow_readme)
    {
        Some(TestWitnessFocusMode::WitnessFocused)
    } else {
        None
    }
}

fn should_skip_test_witness_focus(context: &QueryContext, builder: &SearchIntentBuilder) -> bool {
    builder.has_artifact_bias(ArtifactBias::RuntimeConfigArtifact)
        && !context.has_strong_test_focus_terms()
        && !builder.has_goal(SearchGoal::Examples)
        && !builder.has_goal(SearchGoal::Benchmarks)
}

fn is_runtime_bridge_test_focus(context: &QueryContext, builder: &SearchIntentBuilder) -> bool {
    has_contract_or_error_focus(builder)
        && context.has_any(&["runtime helper", "runtime helpers", "helper", "call site"])
}

fn is_laravel_ui_path_hint_test_focus(builder: &SearchIntentBuilder) -> bool {
    builder.has_artifact_bias(ArtifactBias::LaravelUi)
        && builder.has_goal(SearchGoal::Documentation)
        && !builder.has_goal(SearchGoal::Readme)
        && !has_contract_or_error_focus(builder)
}

fn is_docs_with_strong_test_focus(
    context: &QueryContext,
    builder: &SearchIntentBuilder,
    allow_readme: bool,
) -> bool {
    builder.has_goal(SearchGoal::Documentation)
        && context.has_strong_test_focus_terms()
        && test_witness_focus_readme_allowed(builder, allow_readme)
        && !has_contract_or_error_focus(builder)
}

fn is_plain_test_focus(builder: &SearchIntentBuilder, allow_readme: bool) -> bool {
    !builder.has_goal(SearchGoal::Documentation)
        && test_witness_focus_readme_allowed(builder, allow_readme)
        && !has_contract_or_error_focus(builder)
}

fn test_witness_focus_readme_allowed(builder: &SearchIntentBuilder, allow_readme: bool) -> bool {
    !builder.has_goal(SearchGoal::Readme) || allow_readme
}

fn has_contract_or_error_focus(builder: &SearchIntentBuilder) -> bool {
    builder.has_goal(SearchGoal::Contracts)
        || builder.has_goal(SearchGoal::ErrorTaxonomy)
        || builder.has_goal(SearchGoal::ToolContracts)
}

pub(super) static SEARCH_INTENT_RULES: &[SearchIntentRule] = &[
    SearchIntentRule {
        id: SearchIntentRuleId::DocumentationTerms,
        apply: apply_docs_terms,
    },
    SearchIntentRule {
        id: SearchIntentRuleId::TestsTerms,
        apply: apply_tests_terms,
    },
    SearchIntentRule {
        id: SearchIntentRuleId::ExamplesTerms,
        apply: apply_examples_terms,
    },
    SearchIntentRule {
        id: SearchIntentRuleId::OnboardingTerms,
        apply: apply_onboarding_terms,
    },
    SearchIntentRule {
        id: SearchIntentRuleId::ContractsTerms,
        apply: apply_contract_terms,
    },
    SearchIntentRule {
        id: SearchIntentRuleId::ErrorTaxonomyTerms,
        apply: apply_error_taxonomy_terms,
    },
    SearchIntentRule {
        id: SearchIntentRuleId::ToolContractTerms,
        apply: apply_tool_contract_terms,
    },
    SearchIntentRule {
        id: SearchIntentRuleId::McpRuntimeSurfaceTerms,
        apply: apply_mcp_runtime_surface_terms,
    },
    SearchIntentRule {
        id: SearchIntentRuleId::FixturesTerms,
        apply: apply_fixtures_terms,
    },
    SearchIntentRule {
        id: SearchIntentRuleId::BenchmarksTerms,
        apply: apply_benchmarks_terms,
    },
    SearchIntentRule {
        id: SearchIntentRuleId::RuntimeConfigArtifactTerms,
        apply: apply_runtime_config_artifact_terms,
    },
    SearchIntentRule {
        id: SearchIntentRuleId::LaravelUiWitnessTerms,
        apply: apply_laravel_ui_witness_terms,
    },
    SearchIntentRule {
        id: SearchIntentRuleId::LaravelFormActionWitnessTerms,
        apply: apply_laravel_form_action_witness_terms,
    },
    SearchIntentRule {
        id: SearchIntentRuleId::BladeComponentWitnessTerms,
        apply: apply_blade_component_witness_terms,
    },
    SearchIntentRule {
        id: SearchIntentRuleId::LivewireViewWitnessTerms,
        apply: apply_livewire_view_witness_terms,
    },
    SearchIntentRule {
        id: SearchIntentRuleId::LaravelLayoutWitnessTerms,
        apply: apply_laravel_layout_witness_terms,
    },
    SearchIntentRule {
        id: SearchIntentRuleId::CommandsMiddlewareWitnessTerms,
        apply: apply_commands_middleware_witness_terms,
    },
    SearchIntentRule {
        id: SearchIntentRuleId::JobsListenersWitnessTerms,
        apply: apply_jobs_listeners_witness_terms,
    },
    SearchIntentRule {
        id: SearchIntentRuleId::EntryPointBuildFlowTerms,
        apply: apply_entrypoint_build_flow_terms,
    },
    SearchIntentRule {
        id: SearchIntentRuleId::CiWorkflowWitnessTerms,
        apply: apply_ci_workflow_witness_terms,
    },
    SearchIntentRule {
        id: SearchIntentRuleId::ScriptsOpsWitnessTerms,
        apply: apply_scripts_ops_witness_terms,
    },
    SearchIntentRule {
        id: SearchIntentRuleId::NavigationFallbackTerms,
        apply: apply_navigation_fallback_terms,
    },
    SearchIntentRule {
        id: SearchIntentRuleId::RuntimeWitnessTerms,
        apply: apply_runtime_witness_terms,
    },
    SearchIntentRule {
        id: SearchIntentRuleId::ReadmeTerms,
        apply: apply_readme_terms,
    },
    SearchIntentRule {
        id: SearchIntentRuleId::TestWitnessFocus,
        apply: apply_test_witness_focus,
    },
];
