"""Configuration sync API — enables clients to pull skill/agent/workflow
configs from the server.

Endpoints:
  - GET /api/v1/config/version    — Returns a content hash of all configs.
    Clients poll this every 5 minutes; if the hash changes, they re-pull.
  - GET /api/v1/config/all        — Returns all configs in one response.
  - GET /api/v1/config/skills     — Returns all skill configs.
  - GET /api/v1/config/agents     — Returns all agent configs (from skills).
  - GET /api/v1/config/workflows  — Returns all workflow definitions.

Authentication: requires CHAT permission (low-risk). Dev mode allows access.
"""

from __future__ import annotations

import hashlib
import json
import logging
from typing import Any

from fastapi import APIRouter, Depends, Request

from agentkit.server.auth.dependencies import require_permission
from agentkit.server.auth.permissions import Permission

logger = logging.getLogger(__name__)

router = APIRouter(prefix="/config", tags=["config-sync"])


# ── Helpers ───────────────────────────────────────────────────────────


def _stable_json_hash(data: Any) -> str:
    """Compute a stable SHA-256 hash of JSON-serializable data.

    Keys are sorted to ensure deterministic output regardless of dict
    insertion order.
    """
    encoded = json.dumps(data, sort_keys=True, ensure_ascii=False, default=str)
    return hashlib.sha256(encoded.encode("utf-8")).hexdigest()


def _collect_skill_configs(request: Request) -> list[dict[str, Any]]:
    """Collect all skill configs from the SkillRegistry.

    Returns a list of ``{name, version, config}`` dicts. The ``config``
    field is the full skill config as a dict (from ``SkillConfig.to_dict()``).
    """
    registry = getattr(request.app.state, "skill_registry", None)
    if registry is None:
        return []

    configs: list[dict[str, Any]] = []
    try:
        for name in sorted(registry.list_skills()):
            skill = registry.get(name)
            if skill is None or skill.config is None:
                continue
            config_dict = skill.config.to_dict() if hasattr(skill.config, "to_dict") else {}
            configs.append({
                "name": name,
                "version": getattr(skill.config, "version", "1.0.0"),
                "config": config_dict,
            })
    except Exception as e:
        logger.warning(f"Failed to collect skill configs: {e}")
    return configs


def _collect_workflow_configs(request: Request) -> list[dict[str, Any]]:
    """Collect all workflow definitions from the WorkflowStore.

    Returns a list of workflow definition dicts.
    """
    workflow_store = getattr(request.app.state, "workflow_store", None)
    if workflow_store is None:
        # Try the module-level singleton
        try:
            from agentkit.server.routes.workflows import _workflow_store

            workflow_store = _workflow_store
        except Exception:
            return []

    configs: list[dict[str, Any]] = []
    try:
        workflows = workflow_store.list_workflows()
        for wf in workflows:
            if hasattr(wf, "model_dump"):
                configs.append(wf.model_dump())
            elif hasattr(wf, "to_dict"):
                configs.append(wf.to_dict())
            else:
                configs.append(dict(wf))
    except Exception as e:
        logger.warning(f"Failed to collect workflow configs: {e}")
    return configs


def _compute_config_version(skills: list[dict[str, Any]], workflows: list[dict[str, Any]]) -> str:
    """Compute a single version hash from all config payloads.

    The hash is a SHA-256 of the sorted JSON encoding of both lists.
    Any change to any skill or workflow config produces a different hash.
    """
    payload = {"skills": skills, "workflows": workflows}
    return _stable_json_hash(payload)


# ── Schemas ───────────────────────────────────────────────────────────


class ConfigVersionResponse(dict):
    """Response for GET /config/version.

    ``{"version": "<sha256>", "skill_count": N, "workflow_count": M,
       "computed_at": "<iso>"}``
    """


class ConfigSyncResponse(dict):
    """Response for GET /config/all.

    ``{"version": "<sha256>", "skills": [...], "workflows": [...],
       "synced_at": "<iso>"}``
    """


# ── Endpoints ─────────────────────────────────────────────────────────


@router.get(
    "/version",
    response_model=dict,
    dependencies=[Depends(require_permission(Permission.CHAT))],
)
async def get_config_version(request: Request):
    """Get the current config version hash.

    Clients poll this endpoint every 5 minutes. If the hash differs from
    the locally cached version, the client re-pulls all configs via
    ``GET /config/all``.
    """
    from datetime import datetime, timezone

    skills = _collect_skill_configs(request)
    workflows = _collect_workflow_configs(request)
    version = _compute_config_version(skills, workflows)

    return {
        "version": version,
        "skill_count": len(skills),
        "workflow_count": len(workflows),
        "computed_at": datetime.now(timezone.utc).isoformat(),
    }


@router.get(
    "/all",
    response_model=dict,
    dependencies=[Depends(require_permission(Permission.CHAT))],
)
async def get_all_configs(request: Request):
    """Get all configs (skills + workflows) in a single response.

    This is the primary sync endpoint. The response includes a ``version``
    hash that the client stores alongside the cached configs.
    """
    from datetime import datetime, timezone

    skills = _collect_skill_configs(request)
    workflows = _collect_workflow_configs(request)
    version = _compute_config_version(skills, workflows)

    return {
        "version": version,
        "skills": skills,
        "workflows": workflows,
        "synced_at": datetime.now(timezone.utc).isoformat(),
    }


@router.get(
    "/skills",
    response_model=dict,
    dependencies=[Depends(require_permission(Permission.CHAT))],
)
async def get_skill_configs(request: Request):
    """Get all skill configs."""
    from datetime import datetime, timezone

    skills = _collect_skill_configs(request)
    return {
        "skills": skills,
        "count": len(skills),
        "synced_at": datetime.now(timezone.utc).isoformat(),
    }


@router.get(
    "/agents",
    response_model=dict,
    dependencies=[Depends(require_permission(Permission.CHAT))],
)
async def get_agent_configs(request: Request):
    """Get all agent configs.

    Agents are derived from skills (each skill defines an agent config).
    This endpoint returns the same data as ``/config/skills`` but under
    the ``agents`` key for clients that distinguish between the two.
    """
    from datetime import datetime, timezone

    skills = _collect_skill_configs(request)
    # Agent configs are the skill configs (each skill IS an agent definition)
    agents = [
        {
            "name": s["name"],
            "version": s["version"],
            "config": s["config"],
        }
        for s in skills
    ]
    return {
        "agents": agents,
        "count": len(agents),
        "synced_at": datetime.now(timezone.utc).isoformat(),
    }


@router.get(
    "/workflows",
    response_model=dict,
    dependencies=[Depends(require_permission(Permission.CHAT))],
)
async def get_workflow_configs(request: Request):
    """Get all workflow definitions."""
    from datetime import datetime, timezone

    workflows = _collect_workflow_configs(request)
    return {
        "workflows": workflows,
        "count": len(workflows),
        "synced_at": datetime.now(timezone.utc).isoformat(),
    }
