"""Pure Python data models. No ML dependencies."""
from __future__ import annotations

import uuid
from dataclasses import dataclass, field, asdict
from datetime import datetime, timezone
from enum import Enum
from typing import Any


class BufferStrategy(str, Enum):
    RESERVOIR = "reservoir"
    PRIORITIZED = "prioritized"
    DIVERSE = "diverse"


@dataclass
class EvalResult:
    timestamp: str
    perplexity: float
    loss: float
    dataset_size: int
    label: str = ""

    def to_dict(self) -> dict[str, Any]:
        return asdict(self)

    @classmethod
    def from_dict(cls, d: dict[str, Any]) -> "EvalResult":
        return cls(**d)

    @classmethod
    def now(cls, perplexity: float, loss: float, dataset_size: int, label: str = "") -> "EvalResult":
        return cls(
            timestamp=datetime.now(timezone.utc).isoformat(),
            perplexity=perplexity,
            loss=loss,
            dataset_size=dataset_size,
            label=label,
        )


@dataclass
class AnchorPoint:
    step: int
    eval_result: EvalResult

    def to_dict(self) -> dict[str, Any]:
        return {"step": self.step, "eval_result": self.eval_result.to_dict()}

    @classmethod
    def from_dict(cls, d: dict[str, Any]) -> "AnchorPoint":
        return cls(step=d["step"], eval_result=EvalResult.from_dict(d["eval_result"]))


@dataclass
class RefreshResult:
    cycle_id: str
    timestamp: str
    examples_trained: int
    replay_examples: int
    new_examples: int
    pre_eval: EvalResult | None
    post_eval: EvalResult | None
    bwt: float
    fwt: float
    krs: float
    rolled_back: bool
    rollback_reason: str

    @classmethod
    def new(cls, **kwargs: Any) -> "RefreshResult":
        kwargs.setdefault("cycle_id", str(uuid.uuid4())[:8])
        kwargs.setdefault("timestamp", datetime.now(timezone.utc).isoformat())
        return cls(**kwargs)

    def to_dict(self) -> dict[str, Any]:
        d = asdict(self)
        if self.pre_eval is not None:
            d["pre_eval"] = self.pre_eval.to_dict()
        if self.post_eval is not None:
            d["post_eval"] = self.post_eval.to_dict()
        return d

    @classmethod
    def from_dict(cls, d: dict[str, Any]) -> "RefreshResult":
        d = dict(d)
        if d.get("pre_eval"):
            d["pre_eval"] = EvalResult.from_dict(d["pre_eval"])
        if d.get("post_eval"):
            d["post_eval"] = EvalResult.from_dict(d["post_eval"])
        return cls(**d)
