from __future__ import annotations

import json
from pathlib import Path
from typing import Any

from niannian.storage.cloud_base import resolve_remote_root
from niannian.storage.cloud_tier import CloudTierRouter, MANIFEST_PATH
from niannian.storage.google_drive_api import GoogleDriveClient
from niannian.storage.manifest import utc_now_iso
from niannian.storage.tier_notifier import TierNotifier


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


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


def _build_google_client(store, config: dict[str, Any], cred_mgr) -> GoogleDriveClient | None:
    token = cred_mgr.get_access_token("google_drive")
    if not token:
        return None
    cloud = (config.get("storage") or {}).get("cloud") or {}
    gcfg = cloud.get("google_drive") or {}
    archive_cfg = (config.get("storage") or {}).get("monthly_archive") or {}
    remote_root = archive_cfg.get("remote_root") or resolve_remote_root(config)
    state_path = store.home / "sync" / "google_drive_state.json"
    return GoogleDriveClient(
        token,
        remote_root=remote_root,
        api_base=str(gcfg.get("api_base") or "https://www.googleapis.com/drive/v3"),
        upload_max_bytes=int(gcfg.get("upload_max_bytes") or 5 * 1024 * 1024),
        chunk_size_bytes=int(gcfg.get("chunk_size_bytes") or 8 * 1024 * 1024),
        max_upload_bytes=int(gcfg.get("max_upload_bytes") or 512 * 1024 * 1024),
        credential_manager=cred_mgr,
        state_path=state_path,
    )


def run_monthly_archive(store, *, config: dict[str, Any] | None = None, dry_run: bool = False) -> dict[str, Any]:
    """Best-effort monthly copy of manifest files to Google Drive — no failover impact."""
    config = config or store._config
    archive_cfg = (config.get("storage") or {}).get("monthly_archive") or {}
    if not archive_cfg.get("enabled", True):
        return {"ok": True, "skipped_reason": "disabled", "status": "skipped"}

    from niannian.storage.cloud_auth.manager import CloudCredentialManager

    cred_mgr = CloudCredentialManager(store.home, config=config)
    client = _build_google_client(store, config, cred_mgr)
    if client is None:
        report = {
            "last_run_at": utc_now_iso(),
            "files_ok": 0,
            "files_skipped": 0,
            "errors": ["google_drive not configured"],
            "google_drive_status": "unconfigured",
            "dry_run": dry_run,
        }
        write_monthly_archive_report(store.home, report)
        return {"ok": False, "status": "failed", **report}

    cloud = store._cloud
    source_name = str(archive_cfg.get("source") or "active")
    if source_name == "active" and isinstance(cloud, CloudTierRouter):
        source = cloud.active_backend()
    elif source_name == "quark" and isinstance(cloud, CloudTierRouter):
        source = cloud.quark
    elif isinstance(cloud, CloudTierRouter):
        source = cloud.active_backend()
    else:
        source = cloud

    manifest = store.manifest.read()
    paths = sorted(set(manifest.files.keys()) | {MANIFEST_PATH})
    errors: list[str] = []
    files_ok = 0
    files_skipped = 0

    for rel_path in paths:
        data = source.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 == 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:
            files_skipped += 1
            continue
        if dry_run:
            files_ok += 1
            continue
        try:
            client.upload_object(rel_path, data)
            files_ok += 1
        except Exception as exc:
            errors.append(f"{rel_path}: {exc}")

    g_status = "ok" if not errors else "failed"
    report = {
        "last_run_at": utc_now_iso(),
        "files_ok": files_ok,
        "files_skipped": files_skipped,
        "errors": errors,
        "google_drive_status": g_status,
        "source": source_name,
        "dry_run": dry_run,
    }
    write_monthly_archive_report(store.home, report)

    if errors and archive_cfg.get("best_effort", True):
        notify_cfg = (config.get("notifications") or {}).get("tier") or {}
        TierNotifier(store.home, notify_cfg).emit(
            "monthly_archive_failed",
            provider="google_drive",
            severity="warning",
            message=f"月备失败 {len(errors)} 个文件",
        )

    return {
        "ok": not errors,
        "status": g_status,
        "files_copied": files_ok,
        "files_skipped": files_skipped,
        "errors": errors,
        "google_drive_status": g_status,
    }
