#!/usr/bin/env python3
from __future__ import annotations

import argparse
import http.server
import json
import os
import socketserver
import urllib.error
import urllib.parse
import urllib.request


PROXIES = {
    "/__api_prod__/": "https://api.global-interaction.com/",
    "/__api_test__/": "https://api-test.global-interaction.com/",
    "/__api_local__/": "http://127.0.0.1:13000/",
}

HOP_BY_HOP_HEADERS = {
    "connection",
    "content-length",
    "host",
    "keep-alive",
    "proxy-connection",
    "proxy-authenticate",
    "proxy-authorization",
    "te",
    "trailer",
    "transfer-encoding",
    "upgrade",
}


class H5DevHandler(http.server.SimpleHTTPRequestHandler):
    def do_OPTIONS(self) -> None:
        if self.proxy_base() is None:
            self.send_response(204)
            self.end_headers()
            return
        self.send_response(204)
        self.send_cors_headers()
        self.end_headers()

    def do_GET(self) -> None:
        if self.path == "/__h5_dev_server__/health":
            self.send_response(200)
            self.send_header("Content-Type", "text/plain; charset=utf-8")
            self.send_header("Content-Length", "2")
            self.end_headers()
            self.wfile.write(b"ok")
            return
        self.handle_request()

    def do_HEAD(self) -> None:
        if self.path == "/__h5_dev_server__/health":
            self.send_response(200)
            self.send_header("Content-Type", "text/plain; charset=utf-8")
            self.send_header("Content-Length", "2")
            self.end_headers()
            return
        if self.proxy_base() is None:
            super().do_HEAD()
            return
        self.proxy_request()

    def do_POST(self) -> None:
        self.handle_request()

    def do_PUT(self) -> None:
        self.handle_request()

    def do_PATCH(self) -> None:
        self.handle_request()

    def do_DELETE(self) -> None:
        self.handle_request()

    def handle_request(self) -> None:
        if self.proxy_base() is None:
            super().do_GET()
            return
        self.proxy_request()

    def proxy_base(self) -> tuple[str, str] | None:
        parsed = urllib.parse.urlsplit(self.path)
        for prefix, target in PROXIES.items():
            if parsed.path.startswith(prefix):
                return prefix, target
        return None

    def proxy_request(self) -> None:
        match = self.proxy_base()
        if match is None:
            self.send_error(404)
            return
        prefix, target_base = match
        parsed = urllib.parse.urlsplit(self.path)
        target_path = parsed.path[len(prefix):]
        target_url = urllib.parse.urljoin(target_base, target_path)
        if parsed.query:
            target_url = target_url + "?" + parsed.query

        body = None
        if self.command in {"POST", "PUT", "PATCH"}:
            length = int(self.headers.get("Content-Length", "0") or "0")
            body = self.rfile.read(length) if length > 0 else b""

        # 代理只转发业务相关 header；Host、Connection 等 hop-by-hop header 必须由 urllib 按目标域重建，
        # 否则生产网关会看到本地 Host 或错误的连接语义，排查时会像业务接口异常。
        headers = {
            key: value
            for key, value in self.headers.items()
            if key.lower() not in HOP_BY_HOP_HEADERS
        }
        headers["Accept-Encoding"] = "identity"

        request = urllib.request.Request(
            target_url,
            data=body,
            headers=headers,
            method=self.command,
        )
        try:
            with urllib.request.urlopen(request, timeout=30) as response:
                self.send_proxy_response(response.status, response.headers, response.read())
        except urllib.error.HTTPError as error:
            self.send_proxy_response(error.code, error.headers, error.read())
        except Exception as error:
            body = json.dumps(
                {"code": "PROXY_ERROR", "message": str(error)}
            ).encode("utf-8")
            self.send_response(502)
            self.send_header("Content-Type", "application/json; charset=utf-8")
            self.send_header("Content-Length", str(len(body)))
            self.send_cors_headers()
            self.end_headers()
            self.write_body(body)

    def send_proxy_response(
        self, status: int, headers: http.client.HTTPMessage, body: bytes
    ) -> None:
        self.send_response(status)
        self.copy_response_headers(headers)
        self.send_header("Content-Length", str(len(body)))
        self.send_cors_headers()
        self.end_headers()
        self.write_body(body)

    def write_body(self, body: bytes) -> None:
        if self.command == "HEAD":
            return
        try:
            self.wfile.write(body)
        except BrokenPipeError:
            return

    def copy_response_headers(self, headers: http.client.HTTPMessage) -> None:
        for key, value in headers.items():
            if key.lower() in HOP_BY_HOP_HEADERS:
                continue
            if key.lower() == "content-encoding":
                continue
            self.send_header(key, value)

    def send_cors_headers(self) -> None:
        self.send_header("Access-Control-Allow-Origin", "*")
        self.send_header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")
        self.send_header("Access-Control-Allow-Headers", "Authorization,Content-Type,X-App-Code,X-Request-ID")

    def end_headers(self) -> None:
        self.send_header("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0")
        self.send_header("Pragma", "no-cache")
        self.send_header("Expires", "0")
        super().end_headers()


class ReusableTCPServer(socketserver.ThreadingTCPServer):
    allow_reuse_address = True


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument("--host", default="127.0.0.1")
    parser.add_argument("--port", type=int, default=8788)
    parser.add_argument("--directory", default=os.getcwd())
    args = parser.parse_args()

    handler = lambda *handler_args, **handler_kwargs: H5DevHandler(
        *handler_args,
        directory=args.directory,
        **handler_kwargs,
    )
    with ReusableTCPServer((args.host, args.port), handler) as server:
        print(f"Serving H5 at http://{args.host}:{args.port}/")
        print(f"Manager center: http://{args.host}:{args.port}/gonghui/manager-center/")
        print("API proxy: /__api_prod__/ -> https://api.global-interaction.com/")
        print("API proxy: /__api_test__/ -> https://api-test.global-interaction.com/")
        print("API proxy: /__api_local__/ -> http://127.0.0.1:13000/")
        server.serve_forever()


if __name__ == "__main__":
    main()
