"""MCP server for Navi 3D modeling tools (OpenSCAD)."""

from __future__ import annotations

import json
import os
from typing import Annotated, Any

from mcp.server.fastmcp import FastMCP
from pydantic import Field

from .config import Settings
from .model_compile import compile_scad
from .render_preview import render_stl
from .scad_analyze import lint_scad

INSTRUCTIONS = """
Navi 3D MCP server provides OpenSCAD-based modeling tools.

Use it when the task involves:
- generating 3D models from OpenSCAD scripts (.scad → .stl);
- rendering preview images from STL files;
- linting OpenSCAD source before compilation.

Workflow:
1. Write or read the .scad script via Navi's filesystem tool inside the
   current session directory.
2. Call compile_scad to produce the STL.
3. Call lint_scad before compilation to catch common mistakes.
4. Call render_stl to generate PNG previews.
5. Use content_publish or share_file in Navi to show results to the user.

All paths are session-scoped. Pass the exact Navi session_id so files are
resolved inside session_files/<session_id>/.
""".strip()

mcp = FastMCP("navi-3d", instructions=INSTRUCTIONS)


def _json(data: Any) -> str:
    return json.dumps(data, ensure_ascii=False, indent=2)


def _settings() -> Settings:
    return Settings()


@mcp.tool()
def compile_scad_tool(
    session_id: Annotated[str, Field(description="Navi session ID — files are resolved inside session_files/<session_id>/.")],
    source_path: Annotated[str, Field(description="Relative or absolute path to the .scad file.")],
    output_path: Annotated[str, Field(description="Relative or absolute path for the output STL.")],
) -> str:
    """Compile an OpenSCAD script into a binary STL."""
    result = compile_scad(_settings(), session_id, source_path, output_path)
    return _json(result)


@mcp.tool()
def render_stl_tool(
    session_id: Annotated[str, Field(description="Navi session ID — files are resolved inside session_files/<session_id>/.")],
    source_path: Annotated[str, Field(description="Path to the STL file to render.")],
    views: Annotated[list[str] | None, Field(description="Camera views: front, back, top, bottom, left, right, iso. Max 3.")] = None,
) -> str:
    """Render preview PNG images from an STL file."""
    result = render_stl(_settings(), session_id, source_path, views or ["iso"])
    return _json(result)


@mcp.tool()
def lint_scad_tool(
    session_id: Annotated[str, Field(description="Navi session ID — files are resolved inside session_files/<session_id>/.")],
    source_path: Annotated[str, Field(description="Path to the .scad file.")],
    strict: Annotated[bool, Field(description="Treat warnings as errors.")] = False,
) -> str:
    """Lint an OpenSCAD source file before compiling."""
    result = lint_scad(_settings(), session_id, source_path, strict)
    return _json(result)


def main() -> None:
    transport = os.environ.get("NAVI_3D_MCP_TRANSPORT", "sse")
    if transport not in {"stdio", "sse", "streamable-http"}:
        raise SystemExit("NAVI_3D_MCP_TRANSPORT must be stdio, sse, or streamable-http")
    mcp.run(transport=transport)  # type: ignore[arg-type]


if __name__ == "__main__":
    main()
