"""
Application UI Server — 43-node prime array dashboard.

Hosts an HTML5+Canvas visualization at http://localhost:8000 and bridges the
binary 348-byte stream from engine.py (tcp/8081) and the JSON telemetry
beacon (udp/9000) into a /api/metrics endpoint polled by the front-end.
"""

import http.server
import socketserver
import socket
import threading
import json
import struct
import time

PORT_SERVER = 8000

ui_telemetry_mirror = {
    "energy_invariant_c0": 0.0,
    "active_nodes": 43,
    "status": "BOOTING",
    "math_coords": [0.0] * 43,
    "truth_coords": [0.0] * 43,
}

DASHBOARD_HTML = r"""<!DOCTYPE html>
<html>
<head>
    <title>Absolute Infinity Matrix UI &amp; Vector Canvas</title>
    <style>
        body { background: #060913; color: #e5e7eb; font-family: monospace; padding: 20px; text-align: center; }
        .container { display: flex; flex-direction: row; justify-content: center; align-items: top; gap: 30px; max-width: 1200px; margin: 0 auto; }
        .dashboard-card { background: #0f172a; border: 1px solid #1e293b; padding: 25px; border-radius: 8px; width: 450px; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.5); text-align: left; }
        .canvas-card { background: #0f172a; border: 1px solid #1e293b; padding: 20px; border-radius: 8px; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.5); }
        h2 { color: #34d399; border-bottom: 2px solid #1e293b; padding-bottom: 12px; margin-top: 0; }
        .data-row { font-size: 1.2em; margin: 15px 0; display: flex; justify-content: space-between; }
        .tag { color: #94a3b8; }
        .status-active { color: #34d399; font-weight: bold; }
        .status-fault { color: #f87171; font-weight: bold; }
        canvas { background: #020617; border: 1px solid #334155; border-radius: 4px; }
    </style>
    <script>
        function drawVectorGraph(mathCoords, truthCoords) {
            const canvas = document.getElementById('vectorCanvas');
            const ctx = canvas.getContext('2d');
            const cx = canvas.width / 2;
            const cy = canvas.height / 2;
            const scale = 140;

            ctx.clearRect(0, 0, canvas.width, canvas.height);

            ctx.strokeStyle = '#1e293b';
            ctx.lineWidth = 1;
            ctx.beginPath(); ctx.arc(cx, cy, scale, 0, 2*Math.PI); ctx.stroke();
            ctx.beginPath(); ctx.arc(cx, cy, scale/2, 0, 2*Math.PI); ctx.stroke();

            const screenCoords = [];
            for (let i = 0; i < 43; i++) {
                const angle = (2 * Math.PI * i) / 43;
                const r = scale + (mathCoords[i] * 20);
                const x = cx + r * Math.cos(angle);
                const y = cy + r * Math.sin(angle);
                screenCoords.push({x: x, y: y});
            }

            ctx.strokeStyle = 'rgba(16, 185, 129, 0.4)';
            ctx.lineWidth = 1;
            ctx.beginPath();
            ctx.moveTo(screenCoords[0].x, screenCoords[0].y);
            for (let i = 1; i < 43; i++) {
                ctx.lineTo(screenCoords[i].x, screenCoords[i].y);
            }
            ctx.closePath();
            ctx.stroke();

            for (let i = 0; i < 43; i++) {
                ctx.fillStyle = (i === 42) ? '#f43f5e' : '#34d399';
                ctx.beginPath();
                ctx.arc(screenCoords[i].x, screenCoords[i].y, 4, 0, 2*Math.PI);
                ctx.fill();
            }
        }

        function refreshUIFields() {
            fetch('/api/metrics')
                .then(response => response.json())
                .then(data => {
                    document.getElementById('ui_c0').innerText = data.energy_invariant_c0.toFixed(6);
                    const statusEl = document.getElementById('ui_status');
                    statusEl.innerText = data.status;
                    if (data.status === "MATRIX_LOCKED") {
                        statusEl.className = "status-active";
                        drawVectorGraph(data.math_coords, data.truth_coords);
                    } else {
                        statusEl.className = "status-fault";
                    }
                })
                .catch(() => {
                    document.getElementById('ui_status').innerText = "SERVER_DISCONNECTED";
                    document.getElementById('ui_status').className = "status-fault";
                });
        }
        setInterval(refreshUIFields, 50);
    </script>
</head>
<body>
    <h1>Absolute Infinity Lattice System Workspace</h1>
    <div class="container">
        <div class="dashboard-card">
            <h2>System Metrics Monitor</h2>
            <div class="data-row"><span class="tag">Engine Core Target :</span> <span>localhost:8081</span></div>
            <div class="data-row"><span class="tag">Active Topology Nodes :</span> <span>43 (Prime Field)</span></div>
            <div class="data-row"><span class="tag">Energy Invariant C0 :</span> <span id="ui_c0">0.000000</span></div>
            <div class="data-row"><span class="tag">Pipeline Sync State :</span> <span id="ui_status">CHECKING_LINK</span></div>
        </div>
        <div class="canvas-card">
            <h2>Real-Time HTML5 Canvas Vector Graph</h2>
            <canvas id="vectorCanvas" width="400" height="400"></canvas>
        </div>
    </div>
</body>
</html>
"""


class CoreAppDashboardRequestHandler(http.server.SimpleHTTPRequestHandler):
    def do_GET(self):
        if self.path == "/api/metrics":
            self.send_response(200)
            self.send_header("Content-Type", "application/json")
            self.send_header("Access-Control-Allow-Origin", "*")
            self.end_headers()
            self.wfile.write(json.dumps(ui_telemetry_mirror).encode("utf-8"))
        else:
            self.send_response(200)
            self.send_header("Content-Type", "text/html")
            self.end_headers()
            self.wfile.write(DASHBOARD_HTML.encode("utf-8"))


def listen_to_engine_data_stream():
    while True:
        client_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            client_sock.connect(("127.0.0.1", 8081))
            print("[server] connected to engine binary stream on 8081")
            while True:
                raw_bytes = bytearray()
                while len(raw_bytes) < 348:
                    chunk = client_sock.recv(348 - len(raw_bytes))
                    if not chunk:
                        raise socket.error("engine closed the stream")
                    raw_bytes.extend(chunk)

                offset = 4
                m_list = []
                t_list = []
                for _ in range(43):
                    m_val, t_val = struct.unpack("!ff", raw_bytes[offset:offset + 8])
                    m_list.append(m_val)
                    t_list.append(t_val)
                    offset += 8

                ui_telemetry_mirror["math_coords"] = m_list
                ui_telemetry_mirror["truth_coords"] = t_list
        except socket.error as e:
            ui_telemetry_mirror["status"] = "ENGINE_LINK_DISCONNECTED"
            print(f"[server] engine stream lost: {e}; retrying in 2s")
            time.sleep(2.0)
        finally:
            client_sock.close()


def listen_to_engine_beacon_channel():
    udp_receiver = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    udp_receiver.bind(("127.0.0.1", 9000))
    udp_receiver.settimeout(2.0)
    while True:
        try:
            raw_bytes, _ = udp_receiver.recvfrom(2048)
            decoded = json.loads(raw_bytes.decode("utf-8"))
            ui_telemetry_mirror["energy_invariant_c0"] = decoded["core_matrix"][
                "energy_invariant_c0"
            ]
            if ui_telemetry_mirror["status"] != "ENGINE_LINK_DISCONNECTED":
                ui_telemetry_mirror["status"] = "MATRIX_LOCKED"
        except socket.timeout:
            if ui_telemetry_mirror["status"] != "ENGINE_LINK_DISCONNECTED":
                ui_telemetry_mirror["status"] = "ENGINE_LINK_TIMEOUT"
        except Exception:
            ui_telemetry_mirror["status"] = "TRACKING_FAULT"


if __name__ == "__main__":
    threading.Thread(target=listen_to_engine_beacon_channel, daemon=True).start()
    threading.Thread(target=listen_to_engine_data_stream, daemon=True).start()

    with socketserver.TCPServer(("127.0.0.1", PORT_SERVER), CoreAppDashboardRequestHandler) as server_daemon:
        print(f"[server] dashboard at http://localhost:{PORT_SERVER}")
        server_daemon.serve_forever()
