"""Zo Messaging plugin -- implements MessagingProvider using Zo Computer REST API.

Uses the Zo API (POST https://api.zo.computer/zo/ask) for SMS and email delivery.
Also supports file-based inbox/outbox on Zo's filesystem via the API.

This plugin is the bridge until Vastian has its own SMS/email providers
(Twilio, SendGrid, etc.). It implements the same MessagingProvider protocol
so it can be swapped out without changing any core code.

Requires ZO_API_KEY in .env.
"""

from __future__ import annotations

import logging
from datetime import UTC, datetime
from typing import Any

logger = logging.getLogger(__name__)

ZO_API_URL = "https://api.zo.computer/zo/ask"
ZO_INBOX_PATH = "/home/workspace/vastian-inbox"
ZO_OUTBOX_PATH = "/home/workspace/vastian-outbox"


class ZoMessagingAdapter:
    """MessagingProvider implementation using Zo Computer API.

    Two modes of operation:
    1. Direct API: Uses Zo's REST API to send SMS/email (requires ZO_API_KEY)
    2. File-based: Writes to Zo's outbox dir; a Zo rule handles delivery

    Pull inbox reads from Zo's vastian-inbox/ directory.
    """

    def __init__(self, api_key: str = "") -> None:
        self.api_key = api_key
        self._headers = {
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json",
        }

    async def send_sms(self, body: str, to: str | None = None) -> dict[str, Any]:
        """Send SMS via Zo — MCP first (Mars M1d), REST fallback."""
        if not self.api_key:
            return await self._send_via_outbox("SMS", body)

        try:
            from vastian.mcp.zo_client import send_sms_via_mcp

            return await send_sms_via_mcp(self.api_key, body)
        except Exception:
            pass  # Fall through to REST

        try:
            import httpx

            async with httpx.AsyncClient() as client:
                resp = await client.post(
                    ZO_API_URL,
                    headers=self._headers,
                    json={"input": f"Send this SMS to the user: {body}"},
                    timeout=30,
                )
                data = resp.json()
                return {
                    "ok": True,
                    "message_id": data.get("conversation_id", ""),
                    "response": data.get("output", ""),
                }
        except Exception as e:
            logger.error("Zo SMS send failed: %s", e)
            return await self._send_via_outbox("SMS", body)

    async def send_email(
        self,
        subject: str,
        body: str,
        to: str | None = None,
        attachments: list[str] | None = None,
    ) -> dict[str, Any]:
        """Send email via Zo — MCP first (Mars M1d), REST fallback."""
        if not self.api_key:
            return await self._send_via_outbox("EMAIL", f"{subject}\n{body}")

        try:
            from vastian.mcp.zo_client import send_email_via_mcp

            return await send_email_via_mcp(self.api_key, subject, body)
        except Exception:
            pass  # Fall through to REST

        try:
            import httpx

            async with httpx.AsyncClient() as client:
                resp = await client.post(
                    ZO_API_URL,
                    headers=self._headers,
                    json={
                        "input": f"Send an email to the user with subject '{subject}' and body:\n\n{body}"
                    },
                    timeout=30,
                )
                data = resp.json()
                return {
                    "ok": True,
                    "message_id": data.get("conversation_id", ""),
                    "response": data.get("output", ""),
                }
        except Exception as e:
            logger.error("Zo email send failed: %s", e)
            return await self._send_via_outbox("EMAIL", f"{subject}\n{body}")

    async def pull_inbox(self) -> list[dict[str, Any]]:
        """Pull from Zo vastian-inbox — MCP first (Mars M1d), REST fallback."""
        if not self.api_key:
            logger.debug("No ZO_API_KEY; inbox pull requires MCP or API access")
            return []

        from vastian.mcp.zo_client import pull_inbox_via_mcp

        mcp_messages = await pull_inbox_via_mcp(self.api_key)
        if mcp_messages:
            return mcp_messages

        try:
            import httpx

            async with httpx.AsyncClient() as client:
                # Ask Zo to list and read inbox files
                resp = await client.post(
                    ZO_API_URL,
                    headers=self._headers,
                    json={
                        "input": (
                            f"List all files in {ZO_INBOX_PATH}/ (not subdirectories). "
                            f"For each .txt or .md file, read its content. "
                            f'Return as JSON array: [{{"filename": "...", "content": "..."}}]. '
                            f"If the directory is empty, return []."
                        ),
                        "output_format": {
                            "type": "object",
                            "properties": {
                                "files": {
                                    "type": "array",
                                    "items": {
                                        "type": "object",
                                        "properties": {
                                            "filename": {"type": "string"},
                                            "content": {"type": "string"},
                                        },
                                        "required": ["filename", "content"],
                                    },
                                },
                            },
                            "required": ["files"],
                        },
                    },
                    timeout=30,
                )
                data = resp.json()
                output = data.get("output", {})
                files = output.get("files", []) if isinstance(output, dict) else []
                messages = []
                for f in files:
                    if f.get("filename", "").startswith("."):
                        continue
                    messages.append(
                        {
                            "id": f["filename"],
                            "source": "zo",
                            "content": f["content"],
                            "timestamp": datetime.now(UTC).isoformat(),
                            "type": "file",
                        }
                    )
                return messages
        except Exception as e:
            logger.error("Zo inbox pull failed: %s", e)
            return []

    async def get_contacts(self) -> list[dict[str, str]]:
        """Zo handles contacts internally; return empty."""
        return []

    async def _send_via_outbox(self, msg_type: str, content: str) -> dict[str, Any]:
        """Fallback: write to Zo's outbox for rule-based delivery.

        This works when accessed via MCP but not via REST API.
        """
        logger.info("Zo outbox fallback: writing %s message to outbox", msg_type)
        ts = datetime.now(UTC).strftime("%Y%m%dT%H%M%S")
        filename = f"{msg_type.lower()}-{ts}.txt"
        # This would be called via MCP create_or_rewrite_file in practice
        return {
            "ok": False,
            "message": f"No API key; would write to {ZO_OUTBOX_PATH}/{filename}. Configure ZO_API_KEY for direct delivery.",
            "outbox_file": filename,
            "content": f"{msg_type}:\n{content}",
        }


def register() -> None:
    """Register this plugin with the loader."""
    from vastian.loader import register as loader_register

    loader_register("zo_messaging", ZoMessagingAdapter)
