"""Giga cloud memory sync via native MCP client.

vastian giga push — reads outbox JSONs, calls Giga MCP to write plans/neurons.
vastian giga pull — reads plans/neurons from Giga into local state.
"""

from __future__ import annotations

import json
import logging
from pathlib import Path

from vastian.mcp.client import MCPClient
from vastian.mcp.providers import load_mcp_providers

logger = logging.getLogger(__name__)

# src/vastian/mcp/giga_sync.py -> project root = parents[3]
_PROJECT_ROOT = Path(__file__).resolve().parents[3]
OUTBOX_PLANS = _PROJECT_ROOT / "state" / "giga-outbox" / "plans"
OUTBOX_NEURONS = _PROJECT_ROOT / "state" / "giga-outbox" / "neurons"
SENT_DIR = _PROJECT_ROOT / "state" / "giga-outbox" / "sent"
INBOX_ROOT = _PROJECT_ROOT / "state" / "memory-inbox"

# Giga MCP tool names — may vary by Giga version; list_tools() at runtime can confirm
GIGA_PLAN_TOOL = "write_plan"
GIGA_NEURON_TOOL = "write_neuron"
GIGA_EVOKE_TOOL = "evoke"


def _giga_client(headers: dict[str, str] | None = None) -> MCPClient | None:
    """Build MCP client for Giga if configured and enabled."""
    providers = load_mcp_providers()
    giga = providers.get("giga")
    if not giga or not giga.url:
        return None
    return MCPClient(giga.url, headers=headers or {})


async def giga_push() -> tuple[int, int, list[str]]:
    """Push outbox plans and neurons to Giga via MCP.

    Reads JSON from state/giga-outbox/plans/ and neurons/,
    calls Giga MCP tools, moves success to state/giga-outbox/sent/.

    Returns:
        (plans_pushed, neurons_pushed, errors)
    """
    client = _giga_client()
    if not client:
        return 0, 0, ["Giga MCP not configured — add to config/mcp-providers.yaml"]

    plans_pushed = 0
    neurons_pushed = 0
    errors: list[str] = []

    sent_plans = SENT_DIR / "plans"
    sent_neurons = SENT_DIR / "neurons"
    sent_plans.mkdir(parents=True, exist_ok=True)
    sent_neurons.mkdir(parents=True, exist_ok=True)

    # Push plans
    plan_files = sorted(OUTBOX_PLANS.glob("*.json")) if OUTBOX_PLANS.exists() else []
    for pf in plan_files:
        try:
            data = json.loads(pf.read_text(encoding="utf-8"))
            # Map outbox fields to Giga tool args (schema may vary)
            args = {
                "key_name": data.get("key_name", pf.stem),
                "content": data.get("content", ""),
                "mind": data.get("mind", "vastian-network"),
            }
            result = await client.call_tool(GIGA_PLAN_TOOL, args)
            if "error" in result:
                errors.append(f"Plan {pf.name}: {result['error']}")
                continue
            pf.rename(sent_plans / pf.name)
            plans_pushed += 1
            logger.info("Pushed plan: %s", data.get("key_name", pf.stem))
        except Exception as e:
            errors.append(f"Plan {pf.name}: {e}")

    # Push neurons
    neuron_files = sorted(OUTBOX_NEURONS.glob("*.json")) if OUTBOX_NEURONS.exists() else []
    for nf in neuron_files:
        try:
            data = json.loads(nf.read_text(encoding="utf-8"))
            args = {
                "title": data.get("title", nf.stem),
                "body": data.get("body", ""),
                "selector_nl": data.get("selector_nl", ""),
                "mind": data.get("mind", "vastian-network"),
            }
            result = await client.call_tool(GIGA_NEURON_TOOL, args)
            if "error" in result:
                errors.append(f"Neuron {nf.name}: {result['error']}")
                continue
            nf.rename(sent_neurons / nf.name)
            neurons_pushed += 1
            logger.info("Pushed neuron: %s", data.get("title", nf.stem))
        except Exception as e:
            errors.append(f"Neuron {nf.name}: {e}")

    return plans_pushed, neurons_pushed, errors


async def giga_pull(minds: list[str] | None = None) -> tuple[int, list[str]]:
    """Pull plans and neurons from Giga into local state/memory-inbox.

    Calls Giga evoke (or equivalent) for each mind,
    writes results to state/memory-inbox/{mind}/plans/ and neurons/.

    Args:
        minds: Minds to pull (default: ["vastian-network"]).

    Returns:
        (items_pulled, errors)
    """
    client = _giga_client()
    if not client:
        return 0, ["Giga MCP not configured — add to config/mcp-providers.yaml"]

    minds = minds or ["vastian-network"]
    total = 0
    errors: list[str] = []

    for mind in minds:
        plans_dir = INBOX_ROOT / mind / "plans"
        neurons_dir = INBOX_ROOT / mind / "neurons"
        plans_dir.mkdir(parents=True, exist_ok=True)
        neurons_dir.mkdir(parents=True, exist_ok=True)

        try:
            result = await client.call_tool(GIGA_EVOKE_TOOL, {"mind": mind})
            if "error" in result:
                errors.append(f"Evoke {mind}: {result['error']}")
                continue
            # Giga evoke typically returns structured content
            content = result.get("structured") or result.get("text", "")
            if isinstance(content, dict):
                plans = content.get("plans", [])
                neurons = content.get("neurons", [])
                for i, p in enumerate(plans):
                    outpath = plans_dir / f"giga_plan_{i}.json"
                    outpath.write_text(json.dumps(p, indent=2), encoding="utf-8")
                    total += 1
                for i, n in enumerate(neurons):
                    outpath = neurons_dir / f"giga_neuron_{i}.json"
                    outpath.write_text(json.dumps(n, indent=2), encoding="utf-8")
                    total += 1
            elif content:
                # Fallback: store raw text as one neuron
                outpath = neurons_dir / "giga_evoke_raw.json"
                outpath.write_text(
                    json.dumps({"mind": mind, "content": str(content)}, indent=2),
                    encoding="utf-8",
                )
                total += 1
        except Exception as e:
            errors.append(f"Pull {mind}: {e}")

    return total, errors
