#!/usr/bin/env python3
"""
Sea Dip Community Server — neighbor-facing proxy.

Serves seadip.html and proxies only community API endpoints
to the dashboard server. Blocks access to personal dashboard
and all non-community APIs.

Usage:
    python seadip-server.py

Neighbors access: http://<your-local-ip>:8080
"""

import http.server
import socketserver
import urllib.request
import urllib.error
from pathlib import Path

PORT = 8766
DASHBOARD_ORIGIN = "http://localhost:8765"
BASE_DIR = Path(__file__).parent

# Only these API paths are allowed through to the dashboard server
ALLOWED_API_PREFIXES = (
    "/api/community-members",
    "/api/community-events",
    "/api/community-contacts",
)


class SeaDipHandler(http.server.BaseHTTPRequestHandler):

    def do_OPTIONS(self):
        self.send_response(200)
        self.send_header("Access-Control-Allow-Origin", "*")
        self.send_header("Access-Control-Allow-Methods", "GET, POST, PATCH, DELETE, OPTIONS")
        self.send_header("Access-Control-Allow-Headers", "Content-Type")
        self.end_headers()

    def do_GET(self):
        # Serve seadip.html at root and /seadip.html
        if self.path in ("/", "/seadip.html"):
            self._serve_file(BASE_DIR / "seadip.html", "text/html")
        elif self.path.startswith("/api/"):
            self._proxy("GET")
        else:
            self._send_error(404)

    def do_POST(self):
        if self.path.startswith("/api/"):
            self._proxy("POST")
        else:
            self._send_error(404)

    def do_PATCH(self):
        if self.path.startswith("/api/"):
            self._proxy("PATCH")
        else:
            self._send_error(404)

    def do_DELETE(self):
        if self.path.startswith("/api/"):
            self._proxy("DELETE")
        else:
            self._send_error(404)

    def _send_error(self, code, message="Not found"):
        body = f'{{"error": "{message}"}}'.encode()
        self.send_response(code)
        self.send_header("Content-Type", "application/json")
        self.send_header("Content-Length", str(len(body)))
        self.end_headers()
        self.wfile.write(body)

    def _serve_file(self, filepath, content_type):
        try:
            data = filepath.read_bytes()
            self.send_response(200)
            self.send_header("Content-Type", f"{content_type}; charset=utf-8")
            self.send_header("Content-Length", str(len(data)))
            self.end_headers()
            self.wfile.write(data)
        except FileNotFoundError:
            self.send_error(404, "File not found")

    def _proxy(self, method):
        """Proxy allowed API requests to the dashboard server."""
        # Check if path is in the allowed list
        if not any(self.path.startswith(prefix) for prefix in ALLOWED_API_PREFIXES):
            self._send_error(403, "Forbidden")
            return

        url = f"{DASHBOARD_ORIGIN}{self.path}"

        # Read body for POST/PATCH
        body = None
        if method in ("POST", "PATCH"):
            content_length = int(self.headers.get("Content-Length", 0))
            if content_length > 0:
                body = self.rfile.read(content_length)

        try:
            req = urllib.request.Request(url, data=body, method=method)
            req.add_header("Content-Type", "application/json")

            with urllib.request.urlopen(req, timeout=10) as resp:
                resp_data = resp.read()
                self.send_response(resp.status)
                self.send_header("Content-Type", "application/json")
                self.send_header("Access-Control-Allow-Origin", "*")
                self.end_headers()
                self.wfile.write(resp_data)

        except urllib.error.HTTPError as e:
            self.send_response(e.code)
            self.send_header("Content-Type", "application/json")
            self.end_headers()
            self.wfile.write(e.read())
        except Exception as e:
            self.send_response(502)
            self.send_header("Content-Type", "application/json")
            self.end_headers()
            self.wfile.write(f'{{"error": "Backend unavailable: {e}"}}'.encode())

    def log_message(self, format, *args):
        if "POST" in args[0] or "DELETE" in args[0] or "PATCH" in args[0]:
            print(f"  [{self.client_address[0]}] {args[0]}")


if __name__ == "__main__":
    socketserver.TCPServer.allow_reuse_address = True
    with socketserver.TCPServer(("", PORT), SeaDipHandler) as httpd:
        # Get local IP for display
        import socket
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            s.connect(("8.8.8.8", 80))
            local_ip = s.getsockname()[0]
            s.close()
        except Exception:
            local_ip = "your-local-ip"

        print(f"\n  Sea Dip Community Server")
        print(f"  Local:   http://localhost:{PORT}")
        print(f"  Network: http://{local_ip}:{PORT}")
        print(f"\n  Share this with neighbors: http://{local_ip}:{PORT}")
        print(f"  Admin:   http://{local_ip}:{PORT}/seadip.html?admin=1")
        print(f"\n  Press Ctrl+C to stop\n")
        try:
            httpd.serve_forever()
        except KeyboardInterrupt:
            print("\n  Server stopped.")
