from __future__ import annotations

import json
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Mapping


@dataclass(frozen=True, slots=True)
class PertHours:
    optimistic: float | None
    most_likely: float | None
    pessimistic: float | None
    actual: float | None
    ai_assisted: bool | None
    units: str | None

    def is_complete_triad(self) -> bool:
        return (
            self.optimistic is not None
            and self.most_likely is not None
            and self.pessimistic is not None
        )

    def to_json(self) -> dict:
        return {
            "optimistic": self.optimistic,
            "most_likely": self.most_likely,
            "pessimistic": self.pessimistic,
            "actual": self.actual,
            "ai_assisted": self.ai_assisted,
            "units": self.units,
        }


def load_pert_hours_export(path: Path) -> tuple[str | None, dict[int, PertHours]]:
    """Load `.scarf/pert-hours.json` (PERT Hours Tracker export).

    Returns:
      - units (if present in the export)
      - mapping: issue_number -> PertHours
    """
    raw = json.loads(path.read_text(encoding="utf-8"))
    if not isinstance(raw, dict):
        raise ValueError("PERT export must be a JSON object")

    units = raw.get("units")
    units_str = str(units) if units is not None else None

    issues = raw.get("issues", [])
    if not isinstance(issues, list):
        raise ValueError("PERT export 'issues' must be an array")

    out: dict[int, PertHours] = {}
    for entry in issues:
        if not isinstance(entry, Mapping):
            continue

        issue_number = entry.get("issueNumber")
        if issue_number is None:
            continue
        try:
            n = int(issue_number)
        except (TypeError, ValueError):
            continue

        pert = entry.get("pert", {})
        if not isinstance(pert, Mapping):
            pert = {}

        def _maybe_float(v: Any) -> float | None:
            if v is None:
                return None
            try:
                return float(v)
            except (TypeError, ValueError):
                return None

        optimistic = _maybe_float(pert.get("optimistic"))
        most_likely = _maybe_float(pert.get("mostLikely"))
        pessimistic = _maybe_float(pert.get("pessimistic"))

        actual = _maybe_float(entry.get("trackedActualHours"))

        ai_assisted_raw = entry.get("aiAssisted")
        ai_assisted: bool | None
        if ai_assisted_raw is None:
            ai_assisted = None
        else:
            ai_assisted = bool(ai_assisted_raw)

        out[n] = PertHours(
            optimistic=optimistic,
            most_likely=most_likely,
            pessimistic=pessimistic,
            actual=actual,
            ai_assisted=ai_assisted,
            units=units_str,
        )

    return units_str, out
