from __future__ import annotations

from dataclasses import dataclass
from typing import Any

from niannian.storage.cloud_base import CloudBackend
from niannian.storage.cloud_dual import DualUploadResult
from niannian.storage.manifest import Manifest


@dataclass
class PushResult:
    rel_path: str
    quark_ok: bool

    @property
    def ok(self) -> bool:
        return self.quark_ok


class PrimaryBackupBackend:
    """方案 B — 夸克实时主库；百度仅异步冷备（baidu_backup worker）。"""

    MANIFEST_PATH = "sync/manifest.json"

    def __init__(
        self,
        quark: CloudBackend,
        baidu: CloudBackend,
        *,
        backup_cfg: dict[str, Any] | None = None,
    ) -> None:
        self._quark = quark
        self._baidu = baidu
        self._backup_cfg = backup_cfg or {}
        self._last_degraded = False

    @property
    def quark(self) -> CloudBackend:
        return self._quark

    @property
    def baidu(self) -> CloudBackend:
        return self._baidu

    @property
    def last_upload_degraded(self) -> bool:
        return self._last_degraded

    def primary_mode(self) -> str:
        return self._quark.primary_mode()

    def cloud_ready(self) -> bool:
        return self._quark.is_configured()

    def backend_status(self) -> dict[str, str]:
        return {
            "quark": self._quark.status(),
            "baidu": self._baidu.status(),
            "realtime": "quark",
            "mode": "primary_backup",
        }

    def push_upload(self, rel_path: str, data: bytes) -> PushResult:
        q_ok = self._quark.upload(rel_path, data)
        self._last_degraded = bool(getattr(self._quark, "last_upload_degraded", False))
        return PushResult(rel_path=rel_path, quark_ok=q_ok)

    def pull_download(self, rel_path: str) -> bytes | None:
        return self._quark.download(rel_path)

    def dual_upload(self, rel_path: str, data: bytes) -> DualUploadResult:
        result = self.push_upload(rel_path, data)
        return DualUploadResult(rel_path=rel_path, quark_ok=result.quark_ok, baidu_ok=False)

    def dual_download(self, rel_path: str) -> bytes | None:
        return self.pull_download(rel_path)

    def read_remote_manifest(self) -> Manifest | None:
        import json

        raw = self.pull_download(self.MANIFEST_PATH)
        if raw is None:
            return None
        try:
            return Manifest.from_dict(json.loads(raw.decode("utf-8")))
        except (json.JSONDecodeError, KeyError, TypeError, ValueError):
            return None

    def upload_manifest(self, manifest: Manifest) -> DualUploadResult:
        import json

        data = (json.dumps(manifest.to_dict(), ensure_ascii=False, indent=2) + "\n").encode("utf-8")
        return self.dual_upload(self.MANIFEST_PATH, data)

    def manifest_versions(self) -> dict[str, int | None]:
        import json

        out: dict[str, int | None] = {"quark": None, "baidu": None}
        for name, backend in (("quark", self._quark), ("baidu", self._baidu)):
            raw = backend.download(self.MANIFEST_PATH)
            if raw is None:
                continue
            try:
                out[name] = int(Manifest.from_dict(json.loads(raw.decode("utf-8"))).version)
            except (json.JSONDecodeError, KeyError, TypeError, ValueError):
                continue
        return out

    def backup_status(self, backup_state: dict[str, Any] | None) -> dict[str, Any]:
        state = backup_state or {}
        provider = str(self._backup_cfg.get("provider", "baidu"))
        baidu_live = bool(getattr(self._baidu, "token", None))
        status = state.get("status", "skipped" if not baidu_live else "unknown")
        return {
            "provider": provider,
            "enabled": bool(self._backup_cfg.get("enabled", True)),
            "last_run": state.get("last_baidu_backup_at"),
            "status": status,
            "files_copied": int(state.get("files_ok", 0)),
            "files_skipped": int(state.get("files_skipped", 0)),
            "baidu_configured": baidu_live,
            "skipped_reason": state.get("skipped_reason"),
        }
