from __future__ import annotations

from typing import Any

METRIC_ORDER = (
    "recall_at_k",
    "hit_at_1",
    "mrr",
    "precision_at_k",
    "task_success_rate",
    "utility_rate",
    "noise_rate",
    "memory_helpful_task_success_rate",
    "docs_enabled_ratio",
    "docs_hit_ratio",
    "hint_inject_ratio",
    "multi_channel_rrf_enabled_ratio",
    "multi_channel_rrf_applied_ratio",
    "multi_channel_rrf_granularity_count_mean",
    "multi_channel_rrf_pool_size_mean",
    "multi_channel_rrf_granularity_pool_ratio",
    "multi_channel_rrf_granularity_case_ratio",
    "graph_lookup_enabled_ratio",
    "graph_lookup_guarded_ratio",
    "graph_lookup_log_norm_ratio",
    "graph_lookup_linear_norm_ratio",
    "graph_lookup_boosted_count_mean",
    "graph_lookup_weight_scip_mean",
    "graph_lookup_weight_xref_mean",
    "graph_lookup_weight_query_xref_mean",
    "graph_lookup_weight_symbol_mean",
    "graph_lookup_weight_import_mean",
    "graph_lookup_weight_coverage_mean",
    "graph_lookup_candidate_count_mean",
    "graph_lookup_pool_size_mean",
    "graph_lookup_query_terms_count_mean",
    "graph_lookup_guard_max_candidates_mean",
    "graph_lookup_guard_min_query_terms_mean",
    "graph_lookup_guard_max_query_terms_mean",
    "graph_lookup_query_hit_paths_mean",
    "graph_lookup_scip_signal_paths_mean",
    "graph_lookup_xref_signal_paths_mean",
    "graph_lookup_symbol_hit_paths_mean",
    "graph_lookup_import_hit_paths_mean",
    "graph_lookup_coverage_hit_paths_mean",
    "graph_lookup_max_inbound_mean",
    "graph_lookup_max_xref_count_mean",
    "graph_lookup_max_query_hits_mean",
    "graph_lookup_max_symbol_hits_mean",
    "graph_lookup_max_import_hits_mean",
    "graph_lookup_max_query_coverage_mean",
    "graph_lookup_candidate_count_guard_ratio",
    "graph_lookup_query_terms_too_few_ratio",
    "graph_lookup_query_terms_too_many_ratio",
    "graph_lookup_boosted_path_ratio",
    "graph_lookup_query_hit_path_ratio",
    "deep_symbol_case_count",
    "deep_symbol_case_recall",
    "native_scip_loaded_rate",
    "native_scip_document_count_mean",
    "native_scip_definition_occurrence_count_mean",
    "native_scip_reference_occurrence_count_mean",
    "native_scip_symbol_definition_count_mean",
    "dependency_recall",
    "repomap_worktree_seed_count_mean",
    "repomap_subgraph_seed_count_mean",
    "repomap_seed_candidates_count_mean",
    "repomap_cache_hit_ratio",
    "repomap_precompute_hit_ratio",
    "repomap_latency_p95_ms",
    "repomap_latency_median_ms",
    "latency_p95_ms",
    "latency_median_ms",
    "skills_route_latency_p95_ms",
    "skills_hydration_latency_p95_ms",
    "chunk_hit_at_k",
    "chunks_per_file_mean",
    "chunk_budget_used",
    "retrieval_context_chunk_count_mean",
    "retrieval_context_coverage_ratio",
    "retrieval_context_char_count_mean",
    "contextual_sidecar_parent_symbol_chunk_count_mean",
    "contextual_sidecar_parent_symbol_coverage_ratio",
    "contextual_sidecar_reference_hint_chunk_count_mean",
    "contextual_sidecar_reference_hint_coverage_ratio",
    "chunk_contract_fallback_count_mean",
    "chunk_contract_skeleton_chunk_count_mean",
    "chunk_contract_fallback_ratio",
    "chunk_contract_skeleton_ratio",
    "chunk_cache_contract_present_ratio",
    "chunk_cache_contract_fingerprint_present_ratio",
    "chunk_cache_contract_metadata_aligned_ratio",
    "chunk_cache_contract_file_count_mean",
    "chunk_cache_contract_chunk_count_mean",
    "unsupported_language_fallback_count_mean",
    "unsupported_language_fallback_ratio",
    "subgraph_payload_enabled_ratio",
    "subgraph_seed_path_count_mean",
    "subgraph_edge_type_count_mean",
    "subgraph_edge_total_count_mean",
    "robust_signature_count_mean",
    "robust_signature_coverage_ratio",
    "graph_prior_chunk_count_mean",
    "graph_prior_coverage_ratio",
    "graph_prior_total_mean",
    "graph_seeded_chunk_count_mean",
    "graph_transfer_count_mean",
    "graph_hub_suppressed_chunk_count_mean",
    "graph_hub_penalty_total_mean",
    "graph_closure_enabled_ratio",
    "graph_closure_boosted_chunk_count_mean",
    "graph_closure_coverage_ratio",
    "graph_closure_anchor_count_mean",
    "graph_closure_support_edge_count_mean",
    "graph_closure_total_mean",
    "topological_shield_enabled_ratio",
    "topological_shield_report_only_ratio",
    "topological_shield_attenuated_chunk_count_mean",
    "topological_shield_coverage_ratio",
    "topological_shield_attenuation_total_mean",
    "graph_source_provider_loaded_ratio",
    "graph_source_projection_fallback_ratio",
    "graph_source_edge_count_mean",
    "graph_source_inbound_signal_chunk_count_mean",
    "graph_source_inbound_signal_coverage_ratio",
    "graph_source_centrality_signal_chunk_count_mean",
    "graph_source_centrality_signal_coverage_ratio",
    "graph_source_pagerank_signal_chunk_count_mean",
    "graph_source_pagerank_signal_coverage_ratio",
    "chunk_guard_enabled_ratio",
    "chunk_guard_report_only_ratio",
    "chunk_guard_filtered_count_mean",
    "chunk_guard_filter_ratio",
    "chunk_guard_pairwise_conflict_count_mean",
    "chunk_guard_fallback_ratio",
    "skills_selected_count_mean",
    "skills_token_budget_mean",
    "skills_token_budget_used_mean",
    "skills_budget_exhausted_ratio",
    "skills_skipped_for_budget_mean",
    "skills_metadata_only_routing_ratio",
    "skills_precomputed_route_ratio",
    "skills_manifest_metadata_only_selected_mean",
    "skills_manifest_body_scan_selected_mean",
    "skills_selected_matched_count_mean",
    "skills_selected_signal_count_mean",
    "skills_selected_priority_mean",
    "candidate_rows_materialized_mean",
    "candidate_chunks_materialized_mean",
    "source_plan_candidate_chunks_materialized_mean",
    "skills_markdown_bytes_loaded_mean",
    "budget_abort_ratio",
    "fallback_taken_ratio",
    "plan_replay_cache_enabled_ratio",
    "plan_replay_cache_hit_ratio",
    "plan_replay_cache_stale_hit_safe_ratio",
    "validation_test_count",
    "validation_probe_enabled_ratio",
    "validation_probe_executed_count_mean",
    "validation_probe_failure_rate",
    "validation_branch_case_count",
    "validation_branch_case_rate",
    "validation_branch_candidate_count_mean",
    "validation_branch_rejected_count_mean",
    "validation_branch_selection_present_ratio",
    "validation_branch_patch_artifact_present_ratio",
    "validation_branch_archive_present_ratio",
    "validation_branch_parallel_case_rate",
    "validation_branch_winner_pass_rate",
    "validation_branch_winner_regressed_rate",
    "validation_branch_winner_score_mean",
    "validation_branch_winner_after_issue_count_mean",
    "source_plan_evidence_card_count_mean",
    "source_plan_file_card_count_mean",
    "source_plan_chunk_card_count_mean",
    "source_plan_validation_card_present_ratio",
    "source_plan_validation_feedback_present_ratio",
    "source_plan_validation_feedback_issue_count_mean",
    "source_plan_validation_feedback_failure_rate",
    "source_plan_validation_feedback_probe_issue_count_mean",
    "source_plan_validation_feedback_probe_executed_count_mean",
    "source_plan_validation_feedback_probe_failure_rate",
    "source_plan_validation_feedback_selected_test_count_mean",
    "source_plan_validation_feedback_executed_test_count_mean",
    "source_plan_failure_signal_present_ratio",
    "source_plan_failure_signal_issue_count_mean",
    "source_plan_failure_signal_failure_rate",
    "source_plan_failure_signal_probe_issue_count_mean",
    "source_plan_failure_signal_probe_executed_count_mean",
    "source_plan_failure_signal_probe_failure_rate",
    "source_plan_failure_signal_selected_test_count_mean",
    "source_plan_failure_signal_executed_test_count_mean",
    "source_plan_failure_signal_replay_cache_origin_ratio",
    "source_plan_failure_signal_observability_origin_ratio",
    "source_plan_failure_signal_source_plan_origin_ratio",
    "source_plan_failure_signal_validate_step_origin_ratio",
    "source_plan_direct_evidence_ratio",
    "source_plan_symbol_count_mean",
    "source_plan_signature_count_mean",
    "source_plan_skeleton_count_mean",
    "source_plan_robust_signature_count_mean",
    "source_plan_symbol_ratio",
    "source_plan_signature_ratio",
    "source_plan_skeleton_ratio",
    "source_plan_robust_signature_ratio",
    "source_plan_neighbor_context_ratio",
    "source_plan_hint_only_ratio",
    "source_plan_graph_closure_preference_enabled_ratio",
    "source_plan_graph_closure_bonus_candidate_count_mean",
    "source_plan_graph_closure_preferred_count_mean",
    "source_plan_granularity_preferred_count_mean",
    "source_plan_focused_file_promoted_count_mean",
    "source_plan_packed_path_count_mean",
    "adaptive_router_shadow_coverage",
    "risk_upgrade_precision_gain",
    "evidence_insufficient_rate",
    "no_candidate_rate",
    "low_support_chunk_rate",
    "missing_validation_rate",
    "budget_limited_recovery_rate",
    "noisy_hit_rate",
    "notes_hit_ratio",
    "profile_selected_mean",
    "capture_trigger_ratio",
    "ltm_hit_ratio",
    "ltm_effective_hit_rate",
    "ltm_false_help_rate",
    "ltm_stale_hit_rate",
    "ltm_replay_drift_rate",
    "ltm_latency_overhead_ms",
    "issue_report_linked_plan_rate",
    "issue_to_benchmark_case_conversion_rate",
    "issue_report_time_to_fix_hours_mean",
    "dev_issue_capture_rate",
    "dev_feedback_resolution_rate",
    "dev_issue_to_fix_rate",
    "embedding_enabled_ratio",
    "embedding_similarity_mean",
    "embedding_similarity_max",
    "embedding_rerank_ratio",
    "embedding_cache_hit_ratio",
    "embedding_fallback_ratio",
)

STAGE_LATENCY_ORDER = (
    ("memory", "memory_latency_p95_ms"),
    ("index", "index_latency_p95_ms"),
    ("repomap", "repomap_latency_p95_ms"),
    ("augment", "augment_latency_p95_ms"),
    ("skills", "skills_latency_p95_ms"),
    ("source_plan", "source_plan_latency_p95_ms"),
)

SLO_BUDGET_LIMIT_ORDER = (
    "parallel_time_budget_ms_mean",
    "embedding_time_budget_ms_mean",
    "chunk_semantic_time_budget_ms_mean",
    "xref_time_budget_ms_mean",
)

SLO_SIGNAL_ORDER = (
    "parallel_docs_timeout_ratio",
    "parallel_worktree_timeout_ratio",
    "embedding_time_budget_exceeded_ratio",
    "embedding_adaptive_budget_ratio",
    "embedding_fallback_ratio",
    "chunk_semantic_time_budget_exceeded_ratio",
    "chunk_semantic_fallback_ratio",
    "xref_budget_exhausted_ratio",
    "slo_downgrade_case_rate",
)

ALL_METRIC_ORDER = (
    METRIC_ORDER
    + tuple(metric for _, metric in STAGE_LATENCY_ORDER if metric not in METRIC_ORDER)
    + tuple(metric for metric in SLO_BUDGET_LIMIT_ORDER if metric not in METRIC_ORDER)
    + tuple(metric for metric in SLO_SIGNAL_ORDER if metric not in METRIC_ORDER)
)

COMPARABLE_METRIC_ORDER = ALL_METRIC_ORDER

LATENCY_STYLE_METRICS = {
    "repomap_latency_p95_ms",
    "repomap_latency_median_ms",
    "latency_p95_ms",
    "latency_median_ms",
    "skills_route_latency_p95_ms",
    "skills_hydration_latency_p95_ms",
    "chunk_guard_filtered_count_mean",
    "chunk_guard_pairwise_conflict_count_mean",
    "chunk_contract_fallback_count_mean",
    "chunk_contract_skeleton_chunk_count_mean",
    "chunk_cache_contract_file_count_mean",
    "chunk_cache_contract_chunk_count_mean",
    "unsupported_language_fallback_count_mean",
    "subgraph_seed_path_count_mean",
    "subgraph_edge_type_count_mean",
    "subgraph_edge_total_count_mean",
    "retrieval_context_chunk_count_mean",
    "retrieval_context_char_count_mean",
    "contextual_sidecar_parent_symbol_chunk_count_mean",
    "contextual_sidecar_reference_hint_chunk_count_mean",
    "robust_signature_count_mean",
    "graph_prior_chunk_count_mean",
    "graph_seeded_chunk_count_mean",
    "graph_transfer_count_mean",
    "graph_closure_boosted_chunk_count_mean",
    "graph_closure_anchor_count_mean",
    "graph_closure_support_edge_count_mean",
    "topological_shield_attenuated_chunk_count_mean",
    "graph_source_edge_count_mean",
    "graph_source_inbound_signal_chunk_count_mean",
    "graph_source_centrality_signal_chunk_count_mean",
    "graph_source_pagerank_signal_chunk_count_mean",
    "skills_selected_count_mean",
    "skills_token_budget_mean",
    "skills_token_budget_used_mean",
    "skills_skipped_for_budget_mean",
    "candidate_rows_materialized_mean",
    "candidate_chunks_materialized_mean",
    "source_plan_candidate_chunks_materialized_mean",
    "skills_markdown_bytes_loaded_mean",
    "multi_channel_rrf_granularity_count_mean",
    "multi_channel_rrf_pool_size_mean",
    "graph_lookup_boosted_count_mean",
    "graph_lookup_weight_scip_mean",
    "graph_lookup_weight_xref_mean",
    "graph_lookup_weight_query_xref_mean",
    "graph_lookup_weight_symbol_mean",
    "graph_lookup_weight_import_mean",
    "graph_lookup_weight_coverage_mean",
    "graph_lookup_candidate_count_mean",
    "graph_lookup_pool_size_mean",
    "graph_lookup_query_terms_count_mean",
    "graph_lookup_guard_max_candidates_mean",
    "graph_lookup_guard_min_query_terms_mean",
    "graph_lookup_guard_max_query_terms_mean",
    "graph_lookup_query_hit_paths_mean",
    "graph_lookup_scip_signal_paths_mean",
    "graph_lookup_xref_signal_paths_mean",
    "graph_lookup_symbol_hit_paths_mean",
    "graph_lookup_import_hit_paths_mean",
    "graph_lookup_coverage_hit_paths_mean",
    "graph_lookup_max_inbound_mean",
    "graph_lookup_max_xref_count_mean",
    "graph_lookup_max_query_hits_mean",
    "graph_lookup_max_symbol_hits_mean",
    "graph_lookup_max_import_hits_mean",
    "graph_lookup_max_query_coverage_mean",
    "deep_symbol_case_count",
    "native_scip_document_count_mean",
    "native_scip_definition_occurrence_count_mean",
    "native_scip_reference_occurrence_count_mean",
    "native_scip_symbol_definition_count_mean",
    "repomap_worktree_seed_count_mean",
    "repomap_subgraph_seed_count_mean",
    "repomap_seed_candidates_count_mean",
    "validation_branch_case_count",
    "validation_branch_candidate_count_mean",
    "validation_branch_rejected_count_mean",
    "validation_branch_winner_score_mean",
    "validation_branch_winner_after_issue_count_mean",
    "source_plan_graph_closure_bonus_candidate_count_mean",
    "source_plan_graph_closure_preferred_count_mean",
    "source_plan_granularity_preferred_count_mean",
    "source_plan_focused_file_promoted_count_mean",
    "source_plan_packed_path_count_mean",
    "source_plan_symbol_count_mean",
    "source_plan_signature_count_mean",
    "source_plan_skeleton_count_mean",
    "source_plan_robust_signature_count_mean",
    "ltm_latency_overhead_ms",
    "issue_report_time_to_fix_hours_mean",
    *(metric for _, metric in STAGE_LATENCY_ORDER),
    *SLO_BUDGET_LIMIT_ORDER,
}


def normalize_metrics(raw: Any) -> dict[str, Any]:
    metrics = raw if isinstance(raw, dict) else {}
    normalized = dict(metrics)
    if "task_success_rate" not in normalized and "utility_rate" in normalized:
        normalized["task_success_rate"] = normalized["utility_rate"]
    if "utility_rate" not in normalized and "task_success_rate" in normalized:
        normalized["utility_rate"] = normalized["task_success_rate"]
    return normalized


def format_metric(name: str, value: Any, *, signed: bool = False) -> str:
    number = float(value or 0.0)
    if name in LATENCY_STYLE_METRICS:
        return f"{number:+.2f}" if signed else f"{number:.2f}"
    return f"{number:+.4f}" if signed else f"{number:.4f}"


def format_optional_metric(
    name: str,
    value: Any | None,
    *,
    signed: bool = False,
) -> str:
    if value is None:
        return "-"
    return format_metric(name, value, signed=signed)


def build_zero_metrics() -> dict[str, float]:
    return {metric: 0.0 for metric in ALL_METRIC_ORDER}


__all__ = [
    "ALL_METRIC_ORDER",
    "COMPARABLE_METRIC_ORDER",
    "LATENCY_STYLE_METRICS",
    "METRIC_ORDER",
    "SLO_BUDGET_LIMIT_ORDER",
    "SLO_SIGNAL_ORDER",
    "STAGE_LATENCY_ORDER",
    "build_zero_metrics",
    "format_metric",
    "format_optional_metric",
    "normalize_metrics",
]
