from __future__ import annotations

from typing import Any, Dict, List

from src.strategy_registry import (
    strategy_route_family_alignment,
    strategy_type_priority_for_route,
)


def _safe_float(value: Any, default: float = 0.0) -> float:
    try:
        return float(value or 0.0)
    except (TypeError, ValueError):
        return float(default)


def _selection_score(row: Dict[str, Any], route_family: str = "") -> float:
    route_fit_score = _safe_float(
        row.get(
            "route_fit_score",
            strategy_route_family_alignment(row.get("strategy_id", ""), route_family),
        ),
        0.0,
    )
    route_priority = int(
        row.get(
            "route_priority",
            strategy_type_priority_for_route(row.get("strategy_id", ""), route_family),
        )
        or 0
    )
    base_score = _safe_float(row.get("final_score", 0.0), 0.0)
    return round(base_score + (route_fit_score * 0.25) + (route_priority * 0.02), 4)


def rank_candidate_strategies(
    candidate_rows: List[Dict[str, Any]],
    *,
    route_family: str = "",
) -> List[Dict[str, Any]]:
    eligible_rows = [row for row in candidate_rows if isinstance(row, dict) and bool(row.get("eligible", False))]
    return sorted(
        eligible_rows,
        key=lambda row: (
            _selection_score(row, route_family),
            _safe_float(row.get("health_score", 0.0), 0.0),
            _safe_float(row.get("signal_score", 0.0), 0.0),
            str(row.get("strategy_id", "") or ""),
        ),
        reverse=True,
    )


def select_single_best_strategy(
    candidate_rows: List[Dict[str, Any]],
    *,
    route_family: str = "",
    min_entry_score: float = 0.68,
    min_margin: float = 0.08,
) -> Dict[str, Any]:
    ordered = rank_candidate_strategies(candidate_rows, route_family=route_family)
    winner = ordered[0] if ordered else {}
    runner_up = ordered[1] if len(ordered) > 1 else {}
    winner_score = _selection_score(winner, route_family)
    runner_score = _selection_score(runner_up, route_family)
    margin = round(max(0.0, winner_score - runner_score), 4)

    if not ordered:
        return {
            "passed": False,
            "reason": "NO_ELIGIBLE_CANDIDATE",
            "winner_strategy_id": "",
            "winner_instance_id": "",
            "winner_score": 0.0,
            "runner_up_score": 0.0,
            "selection_margin": 0.0,
            "ranked_candidates": [],
        }

    if winner_score < float(min_entry_score):
        return {
            "passed": False,
            "reason": "WINNER_SCORE_TOO_LOW",
            "winner_strategy_id": str(winner.get("strategy_id", "") or ""),
            "winner_instance_id": str(winner.get("instance_id", "") or ""),
            "winner_score": round(winner_score, 4),
            "runner_up_score": round(runner_score, 4),
            "selection_margin": margin,
            "ranked_candidates": ordered,
        }

    if len(ordered) > 1 and margin < float(min_margin):
        return {
            "passed": False,
            "reason": "NO_DECISIVE_WINNER",
            "winner_strategy_id": str(winner.get("strategy_id", "") or ""),
            "winner_instance_id": str(winner.get("instance_id", "") or ""),
            "winner_score": round(winner_score, 4),
            "runner_up_score": round(runner_score, 4),
            "selection_margin": margin,
            "ranked_candidates": ordered,
        }

    return {
        "passed": True,
        "reason": "SINGLE_BEST_IN_FAMILY",
        "winner_strategy_id": str(winner.get("strategy_id", "") or ""),
        "winner_instance_id": str(winner.get("instance_id", "") or ""),
        "winner_score": round(winner_score, 4),
        "runner_up_score": round(runner_score, 4),
        "selection_margin": margin,
        "ranked_candidates": ordered,
    }


def select_candidate_pool_strategy(
    candidate_rows: List[Dict[str, Any]],
    *,
    route_family: str = "",
    min_entry_score: float = 0.55,
    min_margin: float = 0.02,
) -> Dict[str, Any]:
    ordered = rank_candidate_strategies(candidate_rows, route_family=route_family)
    winner = ordered[0] if ordered else {}
    runner_up = ordered[1] if len(ordered) > 1 else {}
    winner_score = _selection_score(winner, route_family)
    runner_score = _selection_score(runner_up, route_family)
    margin = round(max(0.0, winner_score - runner_score), 4)
    needs_decisive_gate = str(route_family or "").strip().lower() == "uncertain" and len(ordered) > 1

    if not ordered:
        return {
            "passed": False,
            "reason": "NO_ELIGIBLE_CANDIDATE",
            "winner_strategy_id": "",
            "winner_instance_id": "",
            "winner_score": 0.0,
            "runner_up_score": 0.0,
            "selection_margin": 0.0,
            "ranked_candidates": [],
        }

    if needs_decisive_gate and winner_score < float(min_entry_score):
        return {
            "passed": False,
            "reason": "CANDIDATE_POOL_SCORE_TOO_LOW",
            "winner_strategy_id": str(winner.get("strategy_id", "") or ""),
            "winner_instance_id": str(winner.get("instance_id", "") or ""),
            "winner_score": round(winner_score, 4),
            "runner_up_score": round(runner_score, 4),
            "selection_margin": margin,
            "ranked_candidates": ordered,
        }

    if needs_decisive_gate and margin < float(min_margin):
        return {
            "passed": False,
            "reason": "CANDIDATE_POOL_NO_DECISIVE_WINNER",
            "winner_strategy_id": str(winner.get("strategy_id", "") or ""),
            "winner_instance_id": str(winner.get("instance_id", "") or ""),
            "winner_score": round(winner_score, 4),
            "runner_up_score": round(runner_score, 4),
            "selection_margin": margin,
            "ranked_candidates": ordered,
        }

    return {
        "passed": True,
        "reason": "CANDIDATE_POOL_DYNAMIC",
        "winner_strategy_id": str(winner.get("strategy_id", "") or ""),
        "winner_instance_id": str(winner.get("instance_id", "") or ""),
        "winner_score": round(winner_score, 4),
        "runner_up_score": round(runner_score, 4),
        "selection_margin": margin,
        "ranked_candidates": ordered,
    }
