"""µFrame compile, DSL metadata, and image upload endpoints."""

import os
import subprocess
import sys
import tempfile
from pathlib import Path

from fastapi import APIRouter, HTTPException, UploadFile, File
from pydantic import BaseModel

import uframe
import uframe.keywords  # noqa: F401 — triggers keyword registration
from uframe.errors import UFrameError
from uframe.registry import get_dsl_meta

router = APIRouter()

UPLOAD_DIR = Path(os.environ.get("SOURCES_DIR", "/data/sources")) / "images"
BACKEND_DIR = str(Path(__file__).resolve().parent)


def execute_dynamic_script(script: str, timeout: int = 10) -> str:
    """Execute a dynamic page script and return its stdout (micron output).

    Used by both the compile preview and the browse page reader.
    """
    env = {**os.environ, "PYTHONPATH": BACKEND_DIR}
    with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
        f.write(script)
        f.flush()
        try:
            result = subprocess.run(
                [sys.executable, f.name],
                capture_output=True, text=True, timeout=timeout,
                cwd=BACKEND_DIR, env=env,
            )
            if result.returncode != 0 and result.stderr:
                return result.stderr
            return result.stdout
        except subprocess.TimeoutExpired:
            return "Error: script timed out"
        finally:
            os.unlink(f.name)


class CompileRequest(BaseModel):
    source: str
    width: int = 64


class CompileResponse(BaseModel):
    ascii: str
    micron: str
    script: str
    is_dynamic: bool
    warnings: list[str]


@router.post("/compile", response_model=CompileResponse)
async def compile_source(req: CompileRequest):
    """Compile µFrame .uf source into ASCII and Micron output.

    For dynamic pages, the generated script is executed and the
    resolved micron output replaces the static micron in the response.
    """
    try:
        result = uframe.compile(req.source, width=req.width)

        micron = result.micron
        if result.is_dynamic and result.script:
            executed = execute_dynamic_script(result.script)
            # Strip cache header line if present
            lines = executed.split("\n")
            if lines and lines[0].startswith("#!c="):
                lines = lines[1:]
            micron = "\n".join(lines)

        return CompileResponse(
            ascii=result.ascii,
            micron=micron,
            script=result.script,
            is_dynamic=result.is_dynamic,
            warnings=[w.message for w in result.warnings],
        )
    except UFrameError as e:
        raise HTTPException(status_code=422, detail=str(e))


@router.get("/dsl-meta")
async def dsl_meta():
    """Return DSL metadata for frontend syntax highlighting and autocomplete."""
    return get_dsl_meta()


@router.post("/upload-image")
async def upload_image(file: UploadFile = File(...)):
    """Upload an image for use in .uf pages."""
    UPLOAD_DIR.mkdir(parents=True, exist_ok=True)
    # Sanitize filename
    name = file.filename or "upload.png"
    safe_name = "".join(c for c in name if c.isalnum() or c in "._-").rstrip(".")
    if not safe_name:
        safe_name = "upload.png"
    dest = UPLOAD_DIR / safe_name
    content = await file.read()
    dest.write_bytes(content)
    # Return the path relative to backend working directory
    rel_path = str(dest)
    return {"path": rel_path, "filename": safe_name, "size": len(content)}


@router.get("/images")
async def list_images():
    """List uploaded images available for embedding."""
    if not UPLOAD_DIR.is_dir():
        return []
    images = []
    for f in sorted(UPLOAD_DIR.iterdir()):
        if f.suffix.lower() in (".png", ".jpg", ".jpeg", ".bmp", ".webp", ".gif"):
            images.append({
                "filename": f.name,
                "path": str(f),
                "size": f.stat().st_size,
            })
    return images
