#!/usr/bin/env python3
"""
Klaviyo CORS proxy for Mission Control.

Klaviyo blocks direct browser calls to a.klaviyo.com/api/* (CORS preflight rejected).
This script runs a tiny localhost proxy that forwards calls to Klaviyo and adds the
Access-Control-Allow-Origin header so the browser accepts the response.

Run it:                python3 klaviyo-proxy.py
Then in Mission Control:  Settings -> Klaviyo Proxy URL -> http://127.0.0.1:8787/klaviyo

It listens on 127.0.0.1 only, so nothing on your LAN can reach it.
Stop it with Ctrl-C. No dependencies.
"""

from http.server import BaseHTTPRequestHandler, HTTPServer
import urllib.request, urllib.error, sys

HOST = "127.0.0.1"
PORT = 8787
TARGET = "a.klaviyo.com"
PREFIX = "/klaviyo"

class Proxy(BaseHTTPRequestHandler):
    server_version = "klaviyo-proxy/1.0"

    def _cors(self):
        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,revision,content-type,accept,x-klaviyo-user-agent")
        self.send_header("Access-Control-Max-Age", "86400")

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

    def _proxy(self):
        if not self.path.startswith(PREFIX):
            self.send_response(404)
            self._cors()
            self.end_headers()
            self.wfile.write(b'Use ' + PREFIX.encode() + b'/api/...')
            return
        path = self.path[len(PREFIX):] or "/"
        target = f"https://{TARGET}{path}"
        length = int(self.headers.get("Content-Length", "0") or 0)
        body = self.rfile.read(length) if length > 0 else None
        req = urllib.request.Request(target, data=body, method=self.command)
        for h in ("Authorization", "revision", "Content-Type", "Accept"):
            v = self.headers.get(h)
            if v:
                req.add_header(h, v)
        try:
            with urllib.request.urlopen(req, timeout=30) as r:
                self.send_response(r.status)
                for k, v in r.getheaders():
                    if k.lower() in ("transfer-encoding", "content-encoding", "connection",
                                     "access-control-allow-origin", "access-control-allow-headers",
                                     "access-control-allow-methods"):
                        continue
                    self.send_header(k, v)
                self._cors()
                self.end_headers()
                self.wfile.write(r.read())
        except urllib.error.HTTPError as e:
            self.send_response(e.code)
            self._cors()
            self.send_header("content-type", e.headers.get("content-type", "application/json"))
            self.end_headers()
            self.wfile.write(e.read())
        except Exception as e:
            self.send_response(502)
            self._cors()
            self.send_header("content-type", "text/plain")
            self.end_headers()
            self.wfile.write(f"proxy error: {e}".encode())

    do_GET = do_POST = do_PUT = do_PATCH = do_DELETE = _proxy

    def log_message(self, fmt, *a):
        sys.stderr.write(f"{self.command} {self.path} -> {a[1] if len(a) > 1 else '?'}\n")

if __name__ == "__main__":
    print(f"Klaviyo CORS proxy listening on http://{HOST}:{PORT}{PREFIX}/api/...")
    print("In Mission Control -> Settings -> Klaviyo Proxy URL: " f"http://{HOST}:{PORT}{PREFIX}")
    print("Ctrl-C to stop.")
    try:
        HTTPServer((HOST, PORT), Proxy).serve_forever()
    except KeyboardInterrupt:
        sys.exit(0)
