"""One-time migration: export persistent SQLite tables to git-tracked JSON files.

Reads from existing SQLite databases (data/vastian.db, data/tasks.db) and
writes the persistent tables to state/*.json files. Safe to run multiple
times — existing JSON data is preserved (records are merged by ID).

Usage:
    vastian migrate
"""

from __future__ import annotations

import contextlib
import json
import logging
import sqlite3
from pathlib import Path

logger = logging.getLogger(__name__)


def migrate(
    sqlite_path: Path,
    tasks_db_path: Path,
    state_dir: Path,
) -> dict[str, int]:
    """Export persistent SQLite tables to JSON files in state_dir.

    Returns a dict mapping table name → number of records migrated.
    """
    state_dir.mkdir(parents=True, exist_ok=True)
    counts: dict[str, int] = {}

    # ── vastian.db tables ─────────────────────────────
    if sqlite_path.exists():
        try:
            conn = sqlite3.connect(str(sqlite_path))
            conn.row_factory = sqlite3.Row

            # knowledge_entries
            rows = conn.execute("SELECT * FROM knowledge_entries ORDER BY created_at").fetchall()
            records = []
            for r in rows:
                d = dict(r)
                # metadata might be JSON string — parse it
                if isinstance(d.get("metadata"), str):
                    with contextlib.suppress(json.JSONDecodeError, TypeError):
                        d["metadata"] = json.loads(d["metadata"])
                records.append(d)
            _merge_json(state_dir / "knowledge.json", records, "id")
            counts["knowledge_entries"] = len(records)

            # tasks
            rows = conn.execute("SELECT * FROM tasks ORDER BY created_at").fetchall()
            records = []
            for r in rows:
                d = dict(r)
                if isinstance(d.get("metadata"), str):
                    with contextlib.suppress(json.JSONDecodeError, TypeError):
                        d["metadata"] = json.loads(d["metadata"])
                records.append(d)
            _merge_json(state_dir / "tasks.json", records, "id")
            counts["tasks"] = len(records)

            # council_sessions
            rows = conn.execute("SELECT * FROM council_sessions ORDER BY created_at").fetchall()
            records = []
            for r in rows:
                d = dict(r)
                for field in ("members", "transcript", "action_items"):
                    if isinstance(d.get(field), str):
                        with contextlib.suppress(json.JSONDecodeError, TypeError):
                            d[field] = json.loads(d[field])
                records.append(d)
            _merge_json(state_dir / "council-sessions.json", records, "id")
            counts["council_sessions"] = len(records)

            # delegations
            rows = conn.execute("SELECT * FROM delegations ORDER BY created_at").fetchall()
            records = [dict(r) for r in rows]
            _merge_json(state_dir / "delegations.json", records, "id")
            counts["delegations"] = len(records)

            conn.close()
        except Exception as e:
            logger.warning("Failed to migrate from %s: %s", sqlite_path, e)

    # ── tasks.db tables ───────────────────────────────
    if tasks_db_path.exists():
        try:
            conn = sqlite3.connect(str(tasks_db_path))

            # kanban_tasks
            rows = conn.execute("SELECT * FROM kanban_tasks ORDER BY created_at").fetchall()
            records = [
                {
                    "kanban_id": r[0],
                    "suggested_by": r[1],
                    "title": r[2],
                    "description": r[3],
                    "category": r[4],
                    "priority": r[5],
                    "status": r[6],
                    "assigned_to": r[7],
                    "result": r[8],
                    "user_notes": r[9],
                    "created_at": r[10],
                    "approved_at": r[11],
                    "started_at": r[12],
                    "completed_at": r[13],
                    "reviewed_at": r[14] if len(r) > 14 else None,
                }
                for r in rows
            ]
            _merge_json(state_dir / "kanban.json", records, "kanban_id")
            counts["kanban_tasks"] = len(records)

            # delegation_reports
            rows = conn.execute("SELECT * FROM delegation_reports ORDER BY created_at").fetchall()
            records = [
                {
                    "report_id": r[0],
                    "parent_task_id": r[1],
                    "agent_id": r[2],
                    "description": r[3],
                    "full_result": r[4],
                    "tools_used": _safe_json_parse(r[5], []),
                    "documents_written": _safe_json_parse(r[6], []),
                    "status": r[7],
                    "created_at": r[8],
                }
                for r in rows
            ]
            _merge_json(state_dir / "delegation-reports.json", records, "report_id")
            counts["delegation_reports"] = len(records)

            conn.close()
        except Exception as e:
            logger.warning("Failed to migrate from %s: %s", tasks_db_path, e)

    return counts


def _merge_json(
    json_path: Path,
    new_records: list[dict],
    id_field: str,
) -> None:
    """Merge new records into an existing JSON file, deduplicating by ID."""
    existing: list[dict] = []
    if json_path.exists():
        try:
            data = json.loads(json_path.read_text(encoding="utf-8"))
            if isinstance(data, list):
                existing = data
        except (json.JSONDecodeError, FileNotFoundError):
            pass

    # Build lookup of existing IDs
    existing_ids = {r.get(id_field) for r in existing}
    for rec in new_records:
        if rec.get(id_field) not in existing_ids:
            existing.append(rec)
            existing_ids.add(rec.get(id_field))

    json_path.write_text(
        json.dumps(existing, indent=2, ensure_ascii=False, default=str) + "\n",
        encoding="utf-8",
    )


def _safe_json_parse(value: str | None, default: object) -> object:
    """Parse a JSON string, returning default on failure."""
    if not value:
        return default
    try:
        return json.loads(value)
    except (json.JSONDecodeError, TypeError):
        return default
