"""
Cloud backup — upload/download Kodara index to/from Supabase.
"""

from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from kodara.memory.store import KodaraIndex


@dataclass
class BackupInfo:
    id: str
    project_name: str
    file_count: int
    created_at: str


# ── Internal helpers ──────────────────────────────────────────────────────────


def _require_client():
    """Return an authenticated Supabase client or raise with a friendly message."""
    try:
        from kodara.cloud.client import get_client, is_authenticated
    except ImportError as exc:
        raise ImportError(
            "The 'supabase' package is required for cloud features. "
            "Install it with: pip install supabase"
        ) from exc

    if not is_authenticated():
        raise ValueError("Not logged in. Run: kodara login")

    return get_client()


def _current_user_id() -> str:
    """Return the user_id from the stored session."""
    from kodara.cloud.client import load_session

    session = load_session()
    if not session:
        raise ValueError("Not logged in. Run: kodara login")
    # Supabase stores user_id as 'user_id' or inside the JWT; we stored email.
    # The 'sub' claim of the JWT is the user_id. We store it during auth if present.
    user_id = session.get("user_id") or session.get("id") or session.get("email", "")
    if not user_id:
        raise ValueError("Not logged in. Run: kodara login")
    return user_id


# ── Public API ────────────────────────────────────────────────────────────────


def backup(index: "KodaraIndex", user_id: str) -> str:
    """
    Upload index to Supabase.

    1. Upsert project record in ``projects`` table.
    2. Insert index_data (modules + graph_edges) into ``backups`` table.
    3. Keep only last 3 backups per project (delete older ones).

    Returns:
        The backup id (UUID string).
    """
    client = _require_client()

    # ── 1. Upsert project ──────────────────────────────────────────────────
    project_payload = {
        "user_id": user_id,
        "name": index.display_name,
        "repo_id": index.repo_id,
        "source_root": index.source_root,
        "file_count": index.file_count,
        "indexed_at": index.indexed_at,
    }
    # upsert on (user_id, name) so the same project always reuses the same row
    project_resp = (
        client.table("projects")
        .upsert(project_payload, on_conflict="user_id,name")
        .execute()
    )
    project_data = project_resp.data
    if not project_data:
        raise RuntimeError("Failed to upsert project record in Supabase.")
    project_id = project_data[0]["id"]

    # ── 2. Insert backup ───────────────────────────────────────────────────
    index_data = {
        "modules": index.modules,
        "graph_edges": index.graph_edges,
        "file_count": index.file_count,
        "indexed_at": index.indexed_at,
        "display_name": index.display_name,
    }
    backup_resp = (
        client.table("backups")
        .insert({"project_id": project_id, "user_id": user_id, "index_data": index_data})
        .execute()
    )
    backup_data = backup_resp.data
    if not backup_data:
        raise RuntimeError("Failed to insert backup record in Supabase.")
    backup_id = backup_data[0]["id"]

    # ── 3. Keep only last 3 backups per project ────────────────────────────
    all_backups_resp = (
        client.table("backups")
        .select("id, created_at")
        .eq("project_id", project_id)
        .order("created_at", desc=True)
        .execute()
    )
    all_backups = all_backups_resp.data or []
    if len(all_backups) > 3:
        old_ids = [b["id"] for b in all_backups[3:]]
        client.table("backups").delete().in_("id", old_ids).execute()

    return backup_id


def restore(project_name: str, root: Path) -> "KodaraIndex":
    """
    Download the latest backup from Supabase and save it to .kodara/.

    1. Find project by name in ``projects`` table.
    2. Get latest backup from ``backups`` table.
    3. Reconstruct KodaraIndex from index_data.
    4. Save via MemoryStore(root).save(index).

    Returns:
        The restored KodaraIndex.

    Raises:
        ValueError: if the project is not found or no backups exist.
    """
    from kodara.memory.store import KodaraIndex, MemoryStore

    client = _require_client()

    # ── 1. Find project ────────────────────────────────────────────────────
    project_resp = (
        client.table("projects")
        .select("id, name, repo_id, source_root, file_count, indexed_at")
        .eq("name", project_name)
        .limit(1)
        .execute()
    )
    projects = project_resp.data or []
    if not projects:
        raise ValueError(
            f"Project '{project_name}' not found in cloud. "
            "Run 'kodara backups' to see available projects."
        )
    project = projects[0]
    project_id = project["id"]

    # ── 2. Get latest backup ───────────────────────────────────────────────
    backup_resp = (
        client.table("backups")
        .select("id, index_data, created_at")
        .eq("project_id", project_id)
        .order("created_at", desc=True)
        .limit(1)
        .execute()
    )
    backups = backup_resp.data or []
    if not backups:
        raise ValueError(
            f"No backups found for project '{project_name}'."
        )
    index_data = backups[0]["index_data"]

    # ── 3. Reconstruct KodaraIndex ─────────────────────────────────────────
    index = KodaraIndex(
        repo_id=project.get("repo_id", ""),
        display_name=index_data.get("display_name", project_name),
        source_root=project.get("source_root", str(root)),
        indexed_at=float(index_data.get("indexed_at", 0)),
        index_version=3,
        file_count=int(index_data.get("file_count", 0)),
        language_counts={},
        modules=index_data.get("modules", {}),
        graph_edges=index_data.get("graph_edges", []),
        file_hashes={},
    )

    # ── 4. Save locally ────────────────────────────────────────────────────
    store = MemoryStore(root)
    store.save(index)

    return index


def list_backups(user_id: str) -> list[BackupInfo]:
    """
    List all backed-up projects for the current user.

    Returns a list of BackupInfo, one per project (latest backup only).
    Returns an empty list if there are no backups.
    """
    client = _require_client()

    # Get all projects for this user
    projects_resp = (
        client.table("projects")
        .select("id, name, file_count")
        .eq("user_id", user_id)
        .execute()
    )
    projects = projects_resp.data or []
    if not projects:
        return []

    result: list[BackupInfo] = []
    for project in projects:
        # Get the latest backup for this project
        backup_resp = (
            client.table("backups")
            .select("id, created_at")
            .eq("project_id", project["id"])
            .order("created_at", desc=True)
            .limit(1)
            .execute()
        )
        backups = backup_resp.data or []
        if not backups:
            continue
        latest = backups[0]
        result.append(
            BackupInfo(
                id=latest["id"],
                project_name=project["name"],
                file_count=int(project.get("file_count") or 0),
                created_at=latest["created_at"],
            )
        )

    return result
