from __future__ import annotations

import json
from datetime import datetime, timezone
from typing import Any

from niannian.storage.manifest import Manifest, utc_now_iso


def _read_backup_state(home) -> dict[str, Any]:
    path = home / "sync" / "backup_state.json"
    if not path.is_file():
        return {}
    try:
        return json.loads(path.read_text(encoding="utf-8"))
    except (json.JSONDecodeError, OSError):
        return {}


def _write_backup_state(home, state: dict[str, Any]) -> None:
    path = home / "sync" / "backup_state.json"
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(json.dumps(state, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")


def run_baidu_backup(store, *, config: dict[str, Any] | None = None) -> dict[str, Any]:
    """从夸克逐文件复制到百度；百度未配置则跳过。"""
    from niannian.storage.cloud_dual import CloudDualBackend
    from niannian.storage.primary_backup import PrimaryBackupBackend

    config = config or store._config
    primary_cfg = (config.get("storage") or {}).get("primary") or {}
    backup_cfg = primary_cfg.get("backup") or {}
    cloud = store._cloud

    if not isinstance(cloud, (PrimaryBackupBackend, CloudDualBackend)):
        return {"ok": True, "skipped_reason": "unsupported_cloud_backend", "status": "skipped"}

    quark = cloud.quark
    baidu = cloud.baidu
    now = utc_now_iso()

    if not backup_cfg.get("enabled", True):
        state = {"last_baidu_backup_at": now, "status": "skipped", "skipped_reason": "backup_disabled"}
        _write_backup_state(store.home, state)
        return {"ok": True, "status": "skipped", "skipped_reason": "backup_disabled"}

    if not baidu.is_configured():
        state = {
            "last_baidu_backup_at": now,
            "status": "skipped",
            "skipped_reason": "baidu_not_configured",
            "files_ok": 0,
            "files_skipped": 0,
        }
        _write_backup_state(store.home, state)
        return {"ok": True, "status": "skipped", "skipped_reason": "baidu_not_configured"}

    providers = getattr(store, "_providers", {}) or {}
    baidu_prov = providers.get("baidu") or {}
    has_cred = bool(baidu_prov.get("access_token") or baidu_prov.get("refresh_token"))
    if not has_cred and not backup_cfg.get("allow_mirror_without_token", True):
        state = {
            "last_baidu_backup_at": now,
            "status": "skipped",
            "skipped_reason": "baidu_not_configured",
            "files_ok": 0,
            "files_skipped": 0,
        }
        _write_backup_state(store.home, state)
        return {"ok": True, "status": "skipped", "skipped_reason": "baidu_not_configured"}

    if backup_cfg.get("require_quark_live", True) and not quark.is_configured():
        state = {
            "last_baidu_backup_at": now,
            "status": "skipped",
            "skipped_reason": "quark_not_configured",
        }
        _write_backup_state(store.home, state)
        return {"ok": True, "status": "skipped", "skipped_reason": "quark_not_configured"}

    manifest: Manifest = store.manifest.read()
    files_ok = 0
    files_skipped = 0
    errors: list[str] = []

    paths = sorted(set(manifest.files.keys()) | {PrimaryBackupBackend.MANIFEST_PATH})
    for rel_path in paths:
        expected = manifest.files.get(rel_path)
        existing = baidu.download(rel_path)
        if existing is not None and expected is not None:
            import hashlib

            if hashlib.sha256(existing).hexdigest() == expected.sha256:
                files_skipped += 1
                continue
        data = quark.download(rel_path)
        if data is None:
            local_path = store._path_for_rel(rel_path) if hasattr(store, "_path_for_rel") else None
            if rel_path == PrimaryBackupBackend.MANIFEST_PATH:
                local_manifest = store.home / "sync" / "manifest.json"
                if local_manifest.is_file():
                    data = local_manifest.read_bytes()
            elif local_path is not None and local_path.is_file():
                data = local_path.read_bytes()
        if data is None:
            errors.append(f"quark download failed: {rel_path}")
            continue
        if not baidu.upload(rel_path, data):
            errors.append(f"baidu upload failed: {rel_path}")
            continue
        files_ok += 1

    status = "failed" if errors else "ok"
    state = {
        "last_baidu_backup_at": now,
        "status": status,
        "files_ok": files_ok,
        "files_skipped": files_skipped,
        "errors": errors,
    }
    if errors:
        state["skipped_reason"] = "partial_failure"
    _write_backup_state(store.home, state)
    return {
        "ok": not errors,
        "status": status,
        "files_copied": files_ok,
        "files_skipped": files_skipped,
        "errors": errors,
    }


def read_backup_state(home) -> dict[str, Any]:
    return _read_backup_state(home)
