#!/usr/bin/env python3
"""
Tiny local CORS proxy for the Crystalline Heap Forecaster.

Why this exists:
  Browsers can't call Yahoo Finance directly (Yahoo sends no CORS headers),
  and the free public proxies (allorigins / corsproxy.io / thingproxy) are
  currently down, rate-limited, or block file:// origins. This proxy fetches
  the target URL *server-side* (where the browser's same-origin policy does
  not apply), attaches a real browser User-Agent so Yahoo doesn't 401, and
  hands the response back with Access-Control-Allow-Origin: *.

Usage:
  1. python3 yahoo_proxy.py          # leave this running in a terminal
  2. open forecaster_yfinance_event.html and click "PULL LIVE DATA"
  3. Ctrl-C to stop

Requires nothing but the Python 3 standard library.
"""
import sys
import urllib.parse
import urllib.request
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer

PORT = 8787
BROWSER_UA = (
    "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
    "(KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
)


class Proxy(BaseHTTPRequestHandler):
    def _cors(self):
        self.send_header("Access-Control-Allow-Origin", "*")
        self.send_header("Access-Control-Allow-Headers", "*")
        # Lets Chrome's Private Network Access preflight succeed from file:// pages.
        self.send_header("Access-Control-Allow-Private-Network", "true")

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

    def do_GET(self):
        query = urllib.parse.urlparse(self.path).query
        target = urllib.parse.parse_qs(query).get("url", [None])[0]
        if not target:
            self.send_response(400)
            self.send_header("Content-Type", "application/json")
            self._cors()
            self.end_headers()
            self.wfile.write(b'{"error":"missing ?url= parameter"}')
            return
        try:
            req = urllib.request.Request(
                target,
                headers={
                    "User-Agent": BROWSER_UA,
                    "Accept": "application/json,text/plain,*/*",
                },
            )
            with urllib.request.urlopen(req, timeout=15) as upstream:
                body = upstream.read()
                ctype = upstream.headers.get("Content-Type", "application/json")
            self.send_response(200)
            self.send_header("Content-Type", ctype)
            self._cors()
            self.end_headers()
            self.wfile.write(body)
        except Exception as exc:
            self.send_response(502)
            self.send_header("Content-Type", "application/json")
            self._cors()
            self.end_headers()
            msg = str(exc).replace('"', "'")
            self.wfile.write(('{"error":"%s"}' % msg).encode("utf-8"))

    def log_message(self, *args):
        # One terse line per request; comment out for silence.
        sys.stderr.write("[proxy] %s\n" % self.path[:140])


if __name__ == "__main__":
    print("Yahoo CORS proxy listening on http://localhost:%d/?url=..." % PORT)
    print("Leave this running, open the HTML page, click PULL LIVE DATA. Ctrl-C to stop.")
    try:
        ThreadingHTTPServer(("127.0.0.1", PORT), Proxy).serve_forever()
    except KeyboardInterrupt:
        print("\nProxy stopped.")
