"""Deterministic resolver that selects the best dataset source candidate."""

from __future__ import annotations

import hashlib
from dataclasses import dataclass
from typing import Dict, Iterable, List, Optional, Sequence

from model_osmosis.services.data_registry import (
    DataSourceProfile,
    filter_allowed_licenses,
    load_data_registry,
)


@dataclass(frozen=True)
class DataResolution:
    selected: Optional[DataSourceProfile]
    considered: List[Dict[str, object]]
    selection_reason: str

    def to_dict(self) -> Dict[str, object]:
        return {
            "selected": self.selected.to_dict() if self.selected else None,
            "considered": list(self.considered),
            "selection_reason": self.selection_reason,
        }


def _stable_float(seed: str) -> float:
    digest = hashlib.sha256(seed.encode("utf-8")).digest()
    raw = int.from_bytes(digest[:8], "big", signed=False)
    return raw / float(2**64 - 1)


def resolve_data_source(
    *,
    task: str,
    model_ref: str,
    seed: int,
    allowed_licenses: Optional[Sequence[str]] = None,
    registry: Optional[Iterable[DataSourceProfile]] = None,
) -> DataResolution:
    """Select dataset source deterministically based on task + model fingerprint."""
    candidates = list(registry) if registry is not None else load_data_registry(task=task, include_env=True)
    candidates = filter_allowed_licenses(candidates, allowed_licenses)
    if not candidates:
        return DataResolution(
            selected=None,
            considered=[],
            selection_reason="no_candidates_after_license_filter",
        )

    rows: List[Dict[str, object]] = []
    for profile in candidates:
        jitter = _stable_float(f"{model_ref}:{seed}:{profile.dataset_id}:{profile.revision}")
        score = (1.0 / max(1, int(profile.priority))) + (0.3 * float(profile.min_quality)) + (0.05 * jitter)
        rows.append(
            {
                "dataset_id": profile.dataset_id,
                "uri": profile.uri,
                "hub": profile.hub,
                "priority": int(profile.priority),
                "min_quality": float(profile.min_quality),
                "license": profile.license,
                "score": float(score),
                "profile": profile,
            }
        )
    rows.sort(key=lambda item: (-float(item["score"]), int(item["priority"]), str(item["dataset_id"])))
    selected = rows[0]["profile"]
    considered = [
        {
            "dataset_id": str(item["dataset_id"]),
            "hub": str(item["hub"]),
            "priority": int(item["priority"]),
            "license": str(item["license"]),
            "score": round(float(item["score"]), 8),
        }
        for item in rows
    ]
    return DataResolution(
        selected=selected,
        considered=considered,
        selection_reason="best_score",
    )
