"""Nostr publishing via Blossom (NIP-B7) file upload + NIP-94 metadata event.

Blossom is a content-addressed blob storage protocol for Nostr.  This module:
1. Uploads the video file to a Blossom server (NIP-B7 PUT /upload).
2. Publishes a NIP-94 file-metadata event referencing the Blossom URL.

Both operations are optional/degradable:
- If no Blossom server is configured, the upload step is skipped and a
  warning is logged.
- If ``nostr-tools`` (or a compatible library) is not available, the event
  publication step is skipped.

References
----------
- NIP-B7  : https://github.com/hzrd149/blossom
- NIP-94  : https://github.com/nostr-protocol/nips/blob/master/94.md

Usage
-----
    from content.publishing.nostr import publish_episode

    result = await publish_episode(
        video_path="/tmp/episodes/ep001.mp4",
        title="Top Highlights — March 2026",
        description="Today's best moments.",
        tags=["highlights", "gaming"],
    )
"""

from __future__ import annotations

import asyncio
import hashlib
import logging
from dataclasses import dataclass
from pathlib import Path

import httpx

from config import settings

logger = logging.getLogger(__name__)


@dataclass
class NostrPublishResult:
    """Result of a Nostr/Blossom publish attempt."""

    success: bool
    blossom_url: str | None = None
    event_id: str | None = None
    error: str | None = None


def _sha256_file(path: str) -> str:
    """Return the lowercase hex SHA-256 digest of a file."""
    h = hashlib.sha256()
    with open(path, "rb") as fh:
        for chunk in iter(lambda: fh.read(65536), b""):
            h.update(chunk)
    return h.hexdigest()


async def _blossom_upload(video_path: str) -> tuple[bool, str, str]:
    """Upload a video to the configured Blossom server.

    Returns
    -------
    (success, url_or_error, sha256)
    """
    server = settings.content_blossom_server.rstrip("/")
    if not server:
        return False, "CONTENT_BLOSSOM_SERVER not configured", ""

    sha256 = await asyncio.to_thread(_sha256_file, video_path)
    file_size = Path(video_path).stat().st_size
    pubkey = settings.content_nostr_pubkey

    headers: dict[str, str] = {
        "Content-Type": "video/mp4",
        "X-SHA-256": sha256,
        "X-Content-Length": str(file_size),
    }
    if pubkey:
        headers["X-Nostr-Pubkey"] = pubkey

    try:
        async with httpx.AsyncClient(timeout=600) as client:
            with open(video_path, "rb") as fh:
                resp = await client.put(
                    f"{server}/upload",
                    content=fh.read(),
                    headers=headers,
                )
        if resp.status_code in (200, 201):
            data = resp.json()
            url = data.get("url") or f"{server}/{sha256}"
            return True, url, sha256
        return False, f"Blossom upload failed: HTTP {resp.status_code} {resp.text[:200]}", sha256
    except Exception as exc:
        logger.warning("Blossom upload error: %s", exc)
        return False, str(exc), sha256


async def _publish_nip94_event(
    blossom_url: str,
    sha256: str,
    title: str,
    description: str,
    file_size: int,
    tags: list[str],
) -> tuple[bool, str]:
    """Build and publish a NIP-94 file-metadata Nostr event.

    Returns (success, event_id_or_error).
    """
    relay_url = settings.content_nostr_relay
    privkey_hex = settings.content_nostr_privkey

    if not relay_url or not privkey_hex:
        return (
            False,
            "CONTENT_NOSTR_RELAY and CONTENT_NOSTR_PRIVKEY must be configured",
        )

    try:
        # Build NIP-94 event manually to avoid heavy nostr-tools dependency
        import json
        import time

        event_tags = [
            ["url", blossom_url],
            ["x", sha256],
            ["m", "video/mp4"],
            ["size", str(file_size)],
            ["title", title],
        ] + [["t", t] for t in tags]

        event_content = description

        # Minimal NIP-01 event construction
        pubkey = settings.content_nostr_pubkey or ""
        created_at = int(time.time())
        kind = 1063  # NIP-94 file metadata

        serialized = json.dumps(
            [0, pubkey, created_at, kind, event_tags, event_content],
            separators=(",", ":"),
            ensure_ascii=False,
        )
        event_id = hashlib.sha256(serialized.encode()).hexdigest()

        # Sign event (schnorr via secp256k1 not in stdlib; sig left empty for now)
        sig = ""

        event = {
            "id": event_id,
            "pubkey": pubkey,
            "created_at": created_at,
            "kind": kind,
            "tags": event_tags,
            "content": event_content,
            "sig": sig,
        }

        async with httpx.AsyncClient(timeout=30) as client:
            # Send event to relay via NIP-01 websocket-like REST endpoint
            # (some relays accept JSON POST; for full WS support integrate nostr-tools)
            resp = await client.post(
                relay_url.replace("wss://", "https://").replace("ws://", "http://"),
                json=["EVENT", event],
                headers={"Content-Type": "application/json"},
            )
            if resp.status_code in (200, 201):
                return True, event_id
            return False, f"Relay rejected event: HTTP {resp.status_code}"

    except Exception as exc:
        logger.warning("NIP-94 event publication failed: %s", exc)
        return False, str(exc)


async def publish_episode(
    video_path: str,
    title: str,
    description: str = "",
    tags: list[str] | None = None,
) -> NostrPublishResult:
    """Upload video to Blossom and publish NIP-94 metadata event.

    Parameters
    ----------
    video_path:
        Local path to the episode MP4 file.
    title:
        Episode title (used in the NIP-94 event).
    description:
        Episode description.
    tags:
        Hashtag list (without "#") for discoverability.

    Returns
    -------
    NostrPublishResult
        Always returns a result; never raises.
    """
    if not Path(video_path).exists():
        return NostrPublishResult(success=False, error=f"video file not found: {video_path!r}")

    file_size = Path(video_path).stat().st_size
    _tags = tags or []

    # Step 1: Upload to Blossom
    upload_ok, url_or_err, sha256 = await _blossom_upload(video_path)
    if not upload_ok:
        logger.warning("Blossom upload failed (non-fatal): %s", url_or_err)
        return NostrPublishResult(success=False, error=url_or_err)

    blossom_url = url_or_err
    logger.info("Blossom upload successful: %s", blossom_url)

    # Step 2: Publish NIP-94 event
    event_ok, event_id_or_err = await _publish_nip94_event(
        blossom_url, sha256, title, description, file_size, _tags
    )
    if not event_ok:
        logger.warning("NIP-94 event failed (non-fatal): %s", event_id_or_err)
        # Still return partial success — file is uploaded to Blossom
        return NostrPublishResult(
            success=True,
            blossom_url=blossom_url,
            error=f"NIP-94 event failed: {event_id_or_err}",
        )

    return NostrPublishResult(
        success=True,
        blossom_url=blossom_url,
        event_id=event_id_or_err,
    )
