import json
import re
import uuid
from datetime import datetime, timezone
from io import BytesIO
from pathlib import Path
from typing import Optional
from zipfile import ZIP_DEFLATED, ZipFile

import requests


def _slug(text: str) -> str:
    cleaned = re.sub(r"[^a-zA-Z0-9._-]+", "_", text.strip())
    return cleaned.strip("_") or "unknown"


def _zip_slide(slide_path: Path) -> bytes:
    out = BytesIO()
    with ZipFile(out, mode="w", compression=ZIP_DEFLATED) as archive:
        archive.write(slide_path, arcname=slide_path.name)
    return out.getvalue()


def embed(
    slide_path: str,
    model: str,
    key: str,
    server_url: str = "http://127.0.0.1:8000",
    output_dir: Optional[str] = None,
    timeout_seconds: int = 300,
) -> dict:
    """Upload a WSI slide, request an embedding, and save result locally."""
    local_slide = Path(slide_path).expanduser().resolve()
    if not local_slide.exists() or not local_slide.is_file():
        raise FileNotFoundError(f"slide_path not found: {local_slide}")

    zip_blob = _zip_slide(local_slide)
    response = requests.post(
        f"{server_url.rstrip('/')}/v1/embed",
        headers={"X-API-Key": key},
        data={"model": model},
        files={"file": (f"{local_slide.stem}.zip", zip_blob, "application/zip")},
        timeout=timeout_seconds,
    )
    response.raise_for_status()
    payload = response.json()

    now = datetime.now(timezone.utc)
    stamp = now.strftime("%Y%m%dT%H%M%SZ")
    short_id = uuid.uuid4().hex[:8]

    record = {
        "embedding_id": f"{_slug(local_slide.stem)}_{stamp}_{short_id}",
        "saved_at": now.isoformat(),
        "slide_path": str(local_slide),
        "slide_name": local_slide.name,
        "model": model,
        "server_url": server_url.rstrip("/"),
        "backend_response": payload,
        "embedding": payload.get("embedding", []),
    }

    base_output = Path(output_dir).expanduser().resolve() if output_dir else Path.cwd() / "embeddings"
    base_output.mkdir(parents=True, exist_ok=True)
    filename = f"{_slug(local_slide.stem)}__{_slug(model)}__{stamp}__{short_id}.embedding.json"
    save_path = base_output / filename
    save_path.write_text(json.dumps(record, indent=2), encoding="utf-8")
    record["saved_path"] = str(save_path)
    return record
