"""Artifact management tool — create, update, list, and version interactive content."""

from __future__ import annotations

import json
from typing import Any, Callable, Awaitable

from loguru import logger

from flowly.agent.tools.base import Tool


class ArtifactTool(Tool):
    """Action-based tool for managing versioned artifacts."""

    def __init__(
        self,
        store: Any,
        on_change: Callable[[str, dict], Awaitable[None]] | None = None,
    ):
        self._store = store
        self._on_change = on_change

    def set_on_change(self, callback: Callable[[str, dict], Awaitable[None]]) -> None:
        """Set the broadcast callback (wired by CLI after gateway creation)."""
        self._on_change = callback

    # ── Tool interface ────────────────────────────────────────────────────────

    @property
    def name(self) -> str:
        return "artifact"

    @property
    def description(self) -> str:
        return (
            "Create and manage versioned artifacts (HTML, SVG, Markdown, CSV, JSON, "
            "code, Mermaid diagrams, LaTeX, forms, charts).\n\n"
            "Actions:\n"
            "- create: Create a new artifact (type, title, content required)\n"
            "- update: Update existing artifact (artifact_id required; "
            "content change creates version snapshot)\n"
            "- get: Get a single artifact by ID\n"
            "- list: List artifacts with filters (type, pinned, search, limit)\n"
            "- delete: Delete an artifact permanently\n"
            "- pin: Pin/unpin artifact to dashboard\n"
            "- get_versions: Get version history for an artifact\n\n"
            "Artifact types: html, svg, markdown, csv, json, code, mermaid, latex, form, chart\n"
            "Artifacts persist across sessions and are served to client apps."
        )

    @property
    def parameters(self) -> dict[str, Any]:
        return {
            "type": "object",
            "properties": {
                "action": {
                    "type": "string",
                    "description": "The action to perform",
                    "enum": [
                        "create", "update", "get", "list",
                        "delete", "pin", "get_versions",
                    ],
                },
                "artifact_id": {
                    "type": "string",
                    "description": "Artifact ID (for get/update/delete/pin/get_versions)",
                },
                "type": {
                    "type": "string",
                    "description": "Artifact content type",
                    "enum": [
                        "html", "svg", "markdown", "csv", "json",
                        "code", "mermaid", "latex", "form", "chart",
                    ],
                },
                "title": {
                    "type": "string",
                    "description": "Artifact title (max 200 chars)",
                },
                "content": {
                    "type": "string",
                    "description": "Full renderable content (HTML, SVG, Markdown, etc.)",
                },
                "pinned": {
                    "type": "boolean",
                    "description": "Pin to dashboard (for create/update/pin)",
                },
                "dashboard_size": {
                    "type": "string",
                    "description": "Dashboard card size when pinned",
                    "enum": ["small", "medium", "large", "full"],
                },
                "tags": {
                    "type": "array",
                    "items": {"type": "string"},
                    "description": "Tags for categorization",
                },
                "language": {
                    "type": "string",
                    "description": "Programming language for code type (python, javascript, sql, etc.)",
                },
                "search": {
                    "type": "string",
                    "description": "Full-text search query (for list action)",
                },
                "limit": {
                    "type": "integer",
                    "description": "Max results (for list, default 50)",
                },
            },
            "required": ["action"],
        }

    async def execute(self, action: str = "", **kwargs: Any) -> str:
        handlers = {
            "create": self._create,
            "update": self._update,
            "get": self._get,
            "list": self._list,
            "delete": self._delete,
            "pin": self._pin,
            "get_versions": self._get_versions,
        }
        handler = handlers.get(action)
        if not handler:
            return json.dumps({"error": f"Unknown action: {action}. Valid: {list(handlers)}"})

        try:
            return await handler(**kwargs)
        except Exception as exc:
            logger.error("Artifact {} error: {}", action, exc)
            return json.dumps({"error": str(exc), "action": action})

    # ── Actions ───────────────────────────────────────────────────────────────

    async def _create(self, **kw: Any) -> str:
        art_type = kw.get("type", "")
        title = kw.get("title", "")
        content = kw.get("content", "")

        if not art_type:
            return json.dumps({"error": "type is required (html, svg, markdown, form, chart)"})
        if not title:
            return json.dumps({"error": "title is required"})
        if not content:
            return json.dumps({"error": "content is required"})

        # Build metadata from format-specific params
        metadata = kw.get("metadata") or {}
        if kw.get("language"):
            metadata["language"] = kw["language"]

        artifact = self._store.create(
            type=art_type,
            title=title[:200],
            content=content,
            metadata=metadata,
            data_bindings=kw.get("data_bindings"),
            pinned=kw.get("pinned", False),
            dashboard_size=kw.get("dashboard_size", "medium"),
            tags=kw.get("tags"),
            session_key=kw.get("session_key"),
        )

        await self._notify("artifact.created", artifact)

        return json.dumps({
            "action": "create",
            "artifact": _summarize(artifact),
            "message": f"Artifact '{title[:50]}' created (id: {artifact['id']})",
        })

    async def _update(self, **kw: Any) -> str:
        artifact_id = kw.get("artifact_id", "")
        if not artifact_id:
            return json.dumps({"error": "artifact_id is required"})

        old = self._store.get(artifact_id)
        if not old:
            return json.dumps({"error": f"Artifact not found: {artifact_id}"})

        metadata = kw.get("metadata")
        if kw.get("language"):
            metadata = metadata or {}
            metadata["language"] = kw["language"]

        updated = self._store.update(
            artifact_id,
            title=kw.get("title"),
            content=kw.get("content"),
            metadata=metadata,
            data_bindings=kw.get("data_bindings"),
            pinned=kw.get("pinned"),
            dashboard_size=kw.get("dashboard_size"),
            tags=kw.get("tags"),
        )

        version_created = updated and updated["version"] > old["version"]
        await self._notify("artifact.updated", updated or old)

        return json.dumps({
            "action": "update",
            "artifact": _summarize(updated or old),
            "version_created": version_created,
            "message": f"Artifact updated (v{updated['version'] if updated else old['version']})",
        })

    async def _get(self, **kw: Any) -> str:
        artifact_id = kw.get("artifact_id", "")
        if not artifact_id:
            return json.dumps({"error": "artifact_id is required"})

        artifact = self._store.get(artifact_id)
        if not artifact:
            return json.dumps({"error": f"Artifact not found: {artifact_id}"})

        return json.dumps({"action": "get", "artifact": artifact})

    async def _list(self, **kw: Any) -> str:
        results = self._store.list(
            type=kw.get("type"),
            pinned=kw.get("pinned"),
            search=kw.get("search"),
            tags=kw.get("tags"),
            limit=kw.get("limit", 50),
        )

        # Return summaries (no full content) for list view
        summaries = [_summarize(a) for a in results]

        return json.dumps({
            "action": "list",
            "count": len(summaries),
            "artifacts": summaries,
        })

    async def _delete(self, **kw: Any) -> str:
        artifact_id = kw.get("artifact_id", "")
        if not artifact_id:
            return json.dumps({"error": "artifact_id is required"})

        deleted = self._store.delete(artifact_id)
        if not deleted:
            return json.dumps({"error": f"Artifact not found: {artifact_id}"})

        await self._notify("artifact.deleted", {"id": artifact_id})

        return json.dumps({
            "action": "delete",
            "deleted": True,
            "message": f"Artifact {artifact_id} deleted",
        })

    async def _pin(self, **kw: Any) -> str:
        artifact_id = kw.get("artifact_id", "")
        if not artifact_id:
            return json.dumps({"error": "artifact_id is required"})

        pinned = kw.get("pinned", True)
        artifact = self._store.pin(artifact_id, pinned)
        if not artifact:
            return json.dumps({"error": f"Artifact not found: {artifact_id}"})

        await self._notify("artifact.updated", artifact)

        return json.dumps({
            "action": "pin",
            "artifact": _summarize(artifact),
            "message": f"Artifact {'pinned' if pinned else 'unpinned'}",
        })

    async def _get_versions(self, **kw: Any) -> str:
        artifact_id = kw.get("artifact_id", "")
        if not artifact_id:
            return json.dumps({"error": "artifact_id is required"})

        versions = self._store.get_versions(artifact_id)
        # Truncate content in version list for readability
        for v in versions:
            if len(v.get("content", "")) > 200:
                v["content"] = v["content"][:200] + "..."

        return json.dumps({
            "action": "get_versions",
            "artifact_id": artifact_id,
            "count": len(versions),
            "versions": versions,
        })

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

    async def _notify(self, event_name: str, data: dict) -> None:
        """Broadcast event to connected clients (if callback set)."""
        if self._on_change:
            try:
                await self._on_change(event_name, data)
            except Exception as exc:
                logger.debug("Artifact broadcast error: {}", exc)


def _summarize(artifact: dict) -> dict:
    """Return artifact summary without full content (for list views)."""
    summary = {
        "id": artifact["id"],
        "type": artifact.get("type"),
        "title": artifact.get("title"),
        "version": artifact.get("version"),
        "pinned": artifact.get("pinned"),
        "dashboard_size": artifact.get("dashboard_size"),
        "tags": artifact.get("tags", []),
        "created_at": artifact.get("created_at"),
        "updated_at": artifact.get("updated_at"),
    }
    metadata = artifact.get("metadata", {})
    if metadata:
        summary["metadata"] = metadata
    return summary
