"""render_stl — render preview PNGs from an STL file using OpenSCAD."""

from __future__ import annotations

import asyncio
import shutil
from pathlib import Path

from .config import Settings

# Camera presets: (rot_x, rot_y, rot_z)
_CAMERA_PRESETS: dict[str, tuple[int, int, int]] = {
    "front": (0, 0, 0),
    "back": (0, 0, 180),
    "top": (90, 0, 0),
    "bottom": (-90, 0, 0),
    "left": (0, 0, -90),
    "right": (0, 0, 90),
    "iso": (55, 0, 45),
}

_IMG_W, _IMG_H = 400, 300
_MAX_VIEWS = 3


async def render_stl(
    settings: Settings,
    session_id: str,
    source_path: str,
    views: list[str] | None = None,
) -> dict:
    """Render preview PNGs from an STL file.

    Returns a dict with:
    - success (bool)
    - output (str) — human-readable summary
    - error (str | None)
    - metadata (dict) — generated: list[str]
    """
    if not shutil.which(settings.openscad):
        return {
            "success": False,
            "output": "OpenSCAD is not installed on this system.",
            "error": "openscad_not_found",
            "metadata": {},
        }

    try:
        source = settings.resolve_path(session_id, source_path)
    except ValueError as exc:
        return {
            "success": False,
            "output": str(exc),
            "error": "invalid_path",
            "metadata": {},
        }

    if not source.exists():
        return {
            "success": False,
            "output": f"STL file not found: {source}",
            "error": "stl_not_found",
            "metadata": {},
        }
    if not source.is_file():
        return {
            "success": False,
            "output": f"Path is not a file: {source}",
            "error": "not_a_file",
            "metadata": {},
        }

    requested = views or ["iso"]
    if len(requested) > _MAX_VIEWS:
        return {
            "success": False,
            "output": f"Too many views: {len(requested)} (max {_MAX_VIEWS}).",
            "error": "too_many_views",
            "metadata": {},
        }

    invalid = [v for v in requested if v not in _CAMERA_PRESETS]
    if invalid:
        return {
            "success": False,
            "output": (
                f"Unknown views: {invalid}. "
                f"Available: {list(_CAMERA_PRESETS.keys())}."
            ),
            "error": "invalid_views",
            "metadata": {},
        }

    generated: list[str] = []
    errors: list[str] = []

    for view in requested:
        rot_x, rot_y, rot_z = _CAMERA_PRESETS[view]
        out_png = source.with_suffix(f".{view}.png")
        tmp_scad = source.with_suffix(f".{view}.tmp.scad")
        tmp_scad.write_text(f'import("{source}");\n', encoding="utf-8")

        proc = await asyncio.create_subprocess_exec(
            settings.openscad,
            "--camera", f"0,0,0,{rot_x},{rot_y},{rot_z},500",
            "--autocenter",
            "--viewall",
            "--imgsize", f"{_IMG_W},{_IMG_H}",
            "--preview",
            "-o", str(out_png),
            str(tmp_scad),
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.PIPE,
        )
        stdout, stderr = await proc.communicate()

        try:
            tmp_scad.unlink()
        except Exception:
            pass

        if proc.returncode != 0:
            err = stderr.decode(errors="replace").strip() or "OpenSCAD render error"
            errors.append(f"{view}: {err}")
            continue

        if out_png.exists():
            generated.append(str(out_png))
        else:
            errors.append(f"{view}: no PNG produced")

    if errors and not generated:
        return {
            "success": False,
            "output": "All renders failed:\n" + "\n".join(errors),
            "error": "render_failed",
            "metadata": {},
        }

    lines = [f"Generated {len(generated)} image(s):"]
    for p in generated:
        lines.append(f"  {Path(p).name}")
    if errors:
        lines.append("\nErrors:")
        for e in errors:
            lines.append(f"  {e}")

    return {
        "success": bool(generated),
        "output": "\n".join(lines),
        "error": None,
        "metadata": {"generated": generated},
    }
