from __future__ import annotations

from dataclasses import asdict, dataclass
from datetime import datetime, timezone
import json
from pathlib import Path
from typing import Literal

LeaderType = Literal["vault", "wallet"]
LeaderStatus = Literal["discovered", "watchlist", "blocked", "research"]


@dataclass
class DiscoveryRecord:
    leader_id: str
    leader_type: LeaderType
    first_seen_at: str
    last_seen_at: str
    source: str
    status: LeaderStatus = "discovered"
    observation_days: int = 0
    snapshot_count: int = 0
    notes: str = ""


class DiscoveryRegistry:
    """Persistent registry that scores leaders only from first_seen_at forward."""

    def __init__(self, path: str | Path) -> None:
        self.path = Path(path)
        self.records: dict[str, DiscoveryRecord] = {}

    @classmethod
    def load(cls, path: str | Path) -> "DiscoveryRegistry":
        registry = cls(path)
        if registry.path.exists():
            payload = json.loads(registry.path.read_text(encoding="utf-8"))
            for key, row in payload.get("leaders", {}).items():
                registry.records[key] = DiscoveryRecord(**row)
        return registry

    def save(self) -> None:
        self.path.parent.mkdir(parents=True, exist_ok=True)
        payload = {"leaders": {key: asdict(record) for key, record in sorted(self.records.items())}}
        self.path.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")

    def upsert_seen(
        self,
        leader_id: str,
        leader_type: LeaderType,
        source: str,
        seen_at: str | None = None,
        status: LeaderStatus | None = None,
        notes: str | None = None,
    ) -> tuple[DiscoveryRecord, bool]:
        if not leader_id:
            raise ValueError("leader_id is required")
        now = seen_at or datetime.now(timezone.utc).isoformat()
        key = self.key(leader_type, leader_id)
        created = key not in self.records
        if created:
            self.records[key] = DiscoveryRecord(
                leader_id=leader_id,
                leader_type=leader_type,
                first_seen_at=now,
                last_seen_at=now,
                source=source,
                status=status or "discovered",
                snapshot_count=1,
                notes=notes or "",
            )
        else:
            record = self.records[key]
            record.last_seen_at = now
            record.source = source
            record.snapshot_count += 1
            if status is not None:
                record.status = status
            if notes is not None:
                record.notes = notes
            record.observation_days = max(0, self._days_between(record.first_seen_at, now))
        return self.records[key], created

    def records_by_status(self, status: LeaderStatus) -> list[DiscoveryRecord]:
        return [record for record in self.records.values() if record.status == status]

    @staticmethod
    def key(leader_type: LeaderType, leader_id: str) -> str:
        return f"{leader_type}:{leader_id}"

    @staticmethod
    def _days_between(start: str, end: str) -> int:
        start_dt = datetime.fromisoformat(start.replace("Z", "+00:00"))
        end_dt = datetime.fromisoformat(end.replace("Z", "+00:00"))
        return (end_dt.date() - start_dt.date()).days
