"""cortex/storage/queries/webhook_queries.py — Webhook CRUD storage mixin (M20)."""

from __future__ import annotations

import json
from typing import Any, Dict, List, Optional

from cortex.storage._helpers import _now


class WebhookQueryMixin:
    """Mixin providing webhook CRUD and delivery log operations."""

    async def create_webhook(
        self,
        webhook_id: str,
        owner_id: str,
        url: str,
        events: List[str],
        secret: Optional[str] = None,
        name: str = "",
    ) -> Dict[str, Any]:
        """Insert a new webhook row; return it as a dict."""
        async with self._write_lock:
            conn = await self._get_conn()
            now = _now()
            events_json = json.dumps(events)
            name = name or url
            await conn.execute(
                """
                INSERT INTO webhook (id, owner_id, name, url, events, secret, active, created_at, updated_at)
                VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?)
                """,
                (webhook_id, owner_id, name, url, events_json, secret, now, now),
            )
            await conn.commit()
            return await self.get_webhook(webhook_id)

    async def get_webhook(self, webhook_id: str) -> Optional[Dict[str, Any]]:
        """Return a single webhook row as a dict, or None if not found."""
        conn = await self._get_conn()
        async with conn.execute(
            "SELECT * FROM webhook WHERE id = ?", (webhook_id,)
        ) as cur:
            row = await cur.fetchone()
        return dict(row) if row else None

    async def list_webhooks(
        self,
        owner_id: str = "default",
        active_only: bool = False,
    ) -> List[Dict[str, Any]]:
        """Return all webhooks for owner_id."""
        conn = await self._get_conn()
        where = "WHERE owner_id = ?"
        params: List[Any] = [owner_id]
        if active_only:
            where += " AND active = 1"
        async with conn.execute(
            f"SELECT * FROM webhook {where} ORDER BY created_at DESC",
            params,
        ) as cur:
            rows = await cur.fetchall()
        return [dict(r) for r in rows]

    async def update_webhook(
        self,
        webhook_id: str,
        **kwargs: Any,
    ) -> Optional[Dict[str, Any]]:
        """Patch webhook fields (url, events, active, name, secret).

        Only recognised fields are updated; unknown keys are ignored.
        Returns updated row or None if not found.
        """
        allowed = {"url", "events", "active", "name", "secret"}
        updates: Dict[str, Any] = {}
        for k, v in kwargs.items():
            if k in allowed:
                if k == "events" and isinstance(v, list):
                    updates[k] = json.dumps(v)
                else:
                    updates[k] = v

        if not updates:
            return await self.get_webhook(webhook_id)

        async with self._write_lock:
            conn = await self._get_conn()
            now = _now()
            updates["updated_at"] = now
            set_clause = ", ".join(f"{k} = ?" for k in updates)
            values = list(updates.values()) + [webhook_id]
            await conn.execute(
                f"UPDATE webhook SET {set_clause} WHERE id = ?",
                values,
            )
            await conn.commit()
            return await self.get_webhook(webhook_id)

    async def delete_webhook(self, webhook_id: str) -> bool:
        """Delete a webhook by id. Returns True if a row was deleted."""
        async with self._write_lock:
            conn = await self._get_conn()
            async with conn.execute(
                "DELETE FROM webhook WHERE id = ? RETURNING id", (webhook_id,)
            ) as cur:
                row = await cur.fetchone()
            await conn.commit()
            return row is not None

        # ------------------------------------------------------------------ #
        # Webhook delivery log
        # ------------------------------------------------------------------ #

    async def log_webhook_delivery(
        self,
        webhook_id: str,
        event_type: str,
        status_code: Optional[int],
        success: bool,
        error_msg: Optional[str] = None,
    ) -> None:
        """Record a webhook delivery attempt in the webhook_delivery table."""
        async with self._write_lock:
            conn = await self._get_conn()
            # Lazy table creation — idempotent
            await conn.execute(
                """
                CREATE TABLE IF NOT EXISTS webhook_delivery (
                    id          TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
                    webhook_id  TEXT NOT NULL,
                    event_type  TEXT NOT NULL,
                    status_code INTEGER,
                    success     INTEGER NOT NULL,
                    error_msg   TEXT,
                    created_at  TEXT NOT NULL DEFAULT (datetime('now')),
                    FOREIGN KEY (webhook_id) REFERENCES webhook(id) ON DELETE CASCADE
                )
                """
            )
            now = _now()
            await conn.execute(
                """
                INSERT INTO webhook_delivery (webhook_id, event_type, status_code, success, error_msg, created_at)
                VALUES (?, ?, ?, ?, ?, ?)
                """,
                (webhook_id, event_type, status_code, int(success), error_msg, now),
            )
            # Also update last_triggered / last_status on the webhook row
            await conn.execute(
                """
                UPDATE webhook
                SET last_triggered = ?, last_status = ?, failure_count = CASE WHEN ? THEN 0 ELSE failure_count + 1 END
                WHERE id = ?
                """,
                (now, status_code, int(success), webhook_id),
            )
            await conn.commit()
