#!/usr/bin/env python3
"""Dev HTTPS host for the Console add-in (phases P0–P5).

Serves the `addin/` directory over HTTPS (Office.js requires HTTPS) AND
reverse-proxies `/api/*` to the HTTP backend on 127.0.0.1:8765. That lets the
HTTPS taskpane reach the backend **same-origin** — no mixed-content block, no
CORS config, and the live backend is left untouched (it stays HTTP; Ben owns
backend restarts). In P6 the backend serves the add-in itself over HTTPS and
this script is retired.

Env:
  CERT / KEY        office-addin-dev-certs localhost cert + key (required)
  PORT              listen port (default 3443)
  BACKEND_HOST/PORT proxy target (default 127.0.0.1 / 8765)

Local dev only; binds 127.0.0.1.
"""
from __future__ import annotations

import http.client
import os
import ssl
import sys
from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer

HERE = os.path.dirname(os.path.abspath(__file__))
PORT = int(os.environ.get("PORT", "3443"))
CERT = os.environ.get("CERT", "")
KEY = os.environ.get("KEY", "")
BACKEND_HOST = os.environ.get("BACKEND_HOST", "127.0.0.1")
BACKEND_PORT = int(os.environ.get("BACKEND_PORT", "8765"))

_HOP_BY_HOP = {"connection", "keep-alive", "transfer-encoding", "te", "trailer", "upgrade", "proxy-authorization", "proxy-authenticate"}


class Handler(SimpleHTTPRequestHandler):
    def __init__(self, *a, **k):
        super().__init__(*a, directory=HERE, **k)

    def end_headers(self):
        self.send_header("Cache-Control", "no-store")  # always serve fresh add-in code
        super().end_headers()

    # --- reverse proxy for /api/* ---
    def _proxy(self):
        try:
            length = int(self.headers.get("Content-Length", 0) or 0)
            body = self.rfile.read(length) if length else None
            fwd = {k: v for k, v in self.headers.items() if k.lower() not in ("host", "content-length")}
            conn = http.client.HTTPConnection(BACKEND_HOST, BACKEND_PORT, timeout=180)
            conn.request(self.command, self.path, body=body, headers=fwd)
            resp = conn.getresponse()
            data = resp.read()
            self.send_response(resp.status)
            for k, v in resp.getheaders():
                if k.lower() in _HOP_BY_HOP or k.lower() == "content-length":
                    continue
                self.send_header(k, v)
            self.send_header("Content-Length", str(len(data)))
            self.end_headers()
            self.wfile.write(data)
            conn.close()
        except Exception as e:  # backend down / refused → 502 so the add-in shows "unreachable"
            msg = ('{"error":"backend unreachable: %s"}' % type(e).__name__).encode()
            self.send_response(502)
            self.send_header("Content-Type", "application/json")
            self.send_header("Content-Length", str(len(msg)))
            self.end_headers()
            self.wfile.write(msg)

    def do_GET(self):
        if self.path.startswith("/api/"):
            return self._proxy()
        return super().do_GET()

    def do_POST(self):
        if self.path.startswith("/api/"):
            return self._proxy()
        self.send_response(404); self.end_headers()

    def do_PUT(self):
        if self.path.startswith("/api/"):
            return self._proxy()
        self.send_response(404); self.end_headers()

    def do_DELETE(self):
        if self.path.startswith("/api/"):
            return self._proxy()
        self.send_response(404); self.end_headers()

    def do_OPTIONS(self):
        self.send_response(204); self.end_headers()

    def log_message(self, fmt, *args):
        sys.stderr.write("%s %s\n" % (self.log_date_time_string(), fmt % args))


def main():
    if not (CERT and KEY and os.path.exists(CERT) and os.path.exists(KEY)):
        sys.stderr.write(f"FATAL: CERT/KEY missing or not found (CERT={CERT!r} KEY={KEY!r})\n")
        sys.exit(2)
    ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
    ctx.load_cert_chain(CERT, KEY)
    httpd = ThreadingHTTPServer(("127.0.0.1", PORT), Handler)
    httpd.socket = ctx.wrap_socket(httpd.socket, server_side=True)
    sys.stderr.write(
        f"Console add-in dev host: https://localhost:{PORT}  (serving {HERE}; "
        f"proxy /api/* -> http://{BACKEND_HOST}:{BACKEND_PORT})\n"
    )
    httpd.serve_forever()


if __name__ == "__main__":
    main()
