"""
Three.js web viewer for PHG OBJ exports.
"""

from __future__ import annotations

import html
import os
from functools import partial
from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
from typing import Optional


def _relpath_for_url(path: str, base_dir: str) -> str:
    rel = os.path.relpath(os.path.abspath(path), base_dir)
    return rel.replace("\\", "/")


def write_three_view_html(obj_path: str, out_html: str, title: str = "PHG Web Viewer") -> str:
    base_dir = os.path.dirname(os.path.abspath(out_html))
    rel_obj = _relpath_for_url(obj_path, base_dir)

    doc = f"""<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>{html.escape(title)}</title>
  <style>
    :root {{
      --bg: #f6f3ee;
      --panel: #ffffff;
      --text: #1d1d1f;
      --line: #dcd7cf;
      --accent: #1b5e6b;
    }}
    * {{ box-sizing: border-box; }}
    body {{
      margin: 0;
      background: var(--bg);
      color: var(--text);
      font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif;
    }}
    #app {{
      position: fixed;
      inset: 0;
    }}
    canvas {{ display: block; }}
    .hud {{
      position: absolute;
      top: 16px;
      left: 16px;
      background: rgba(255,255,255,0.9);
      border: 1px solid var(--line);
      border-radius: 10px;
      padding: 10px 12px;
      box-shadow: 0 6px 18px rgba(0,0,0,0.08);
      font-size: 12px;
      line-height: 1.5;
    }}
    .hud strong {{ color: var(--accent); }}
  </style>
</head>
<body>
  <div id="app"></div>
  <div class="hud">
    <div><strong>PHG Web Viewer</strong></div>
    <div>OBJ: {html.escape(os.path.basename(obj_path))}</div>
    <div>Controls: LMB rotate, RMB pan, wheel zoom</div>
  </div>
  <script type="importmap">
    {{
      "imports": {{
        "three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js",
        "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/"
      }}
    }}
  </script>
  <script type="module">
    import * as THREE from "three";
    import {{ OrbitControls }} from "three/addons/controls/OrbitControls.js";
    import {{ OBJLoader }} from "three/addons/loaders/OBJLoader.js";

    const container = document.getElementById("app");
    const scene = new THREE.Scene();
    scene.background = new THREE.Color(0xf6f3ee);

    const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 2000);
    camera.position.set(6, 6, 10);

    const renderer = new THREE.WebGLRenderer({{ antialias: true }});
    renderer.setPixelRatio(window.devicePixelRatio || 1);
    renderer.setSize(window.innerWidth, window.innerHeight);
    container.appendChild(renderer.domElement);

    const controls = new OrbitControls(camera, renderer.domElement);
    controls.enableDamping = true;
    controls.dampingFactor = 0.08;

    const lightA = new THREE.DirectionalLight(0xffffff, 0.9);
    lightA.position.set(6, 10, 8);
    scene.add(lightA);
    scene.add(new THREE.AmbientLight(0xffffff, 0.35));

    const loader = new OBJLoader();
    loader.load("{html.escape(rel_obj)}", (obj) => {{
      obj.traverse((child) => {{
        if (child.isMesh) {{
          child.material = new THREE.MeshStandardMaterial({{
            color: 0x9fb3c8,
            roughness: 0.55,
            metalness: 0.05
          }});
        }}
      }});
      scene.add(obj);

      const box = new THREE.Box3().setFromObject(obj);
      const size = box.getSize(new THREE.Vector3());
      const center = box.getCenter(new THREE.Vector3());
      const maxDim = Math.max(size.x, size.y, size.z) || 1;
      const camDist = maxDim * 2.2;
      camera.position.set(center.x + camDist, center.y + camDist, center.z + camDist);
      controls.target.copy(center);
      controls.update();
    }});

    function onResize() {{
      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(window.innerWidth, window.innerHeight);
    }}
    window.addEventListener("resize", onResize);

    function animate() {{
      requestAnimationFrame(animate);
      controls.update();
      renderer.render(scene, camera);
    }}
    animate();
  </script>
</body>
</html>
"""
    with open(out_html, "w", encoding="utf-8") as f:
        f.write(doc)
    return out_html


def serve_three_view(
    obj_path: str,
    html_path: Optional[str] = None,
    host: str = "127.0.0.1",
    port: int = 8766,
    title: str = "PHG Web Viewer",
) -> str:
    if html_path is None:
        base, _ = os.path.splitext(obj_path)
        html_path = base + ".html"
    write_three_view_html(obj_path, html_path, title=title)

    directory = os.path.dirname(os.path.abspath(html_path)) or os.getcwd()
    handler = partial(SimpleHTTPRequestHandler, directory=directory)
    server = ThreadingHTTPServer((host, port), handler)
    url = f"http://{host}:{port}/{os.path.basename(html_path)}"
    print(url)
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        pass
    finally:
        server.server_close()
    return url
