import { useEffect, useRef, useState } from "react";
import Globe from "react-globe.gl";
import { motion } from "framer-motion";
import {
  Activity,
  Gauge,
  Orbit,
  Radio,
  RefreshCcw,
  Satellite,
  Signal,
} from "lucide-react";

const TASKS = ["easy", "medium", "hard"];
const STEP_INTERVAL_MS = 2200;
const explicitApiBase = import.meta.env.VITE_API_BASE?.replace(/\/$/, "");
const isLocalPreview =
  typeof window !== "undefined" &&
  ["localhost", "127.0.0.1"].includes(window.location.hostname) &&
  window.location.port === "4173";
const API_BASE = explicitApiBase || (isLocalPreview ? "http://127.0.0.1:8000" : "");
const EARTH_TEXTURE = "https://unpkg.com/three-globe/example/img/earth-blue-marble.jpg";
const EARTH_BUMP = "https://unpkg.com/three-globe/example/img/earth-topology.png";
const STARFIELD = "https://unpkg.com/three-globe/example/img/night-sky.png";

function apiUrl(path) {
  return `${API_BASE}${path}`;
}

function useDemoData() {
  const [snapshot, setSnapshot] = useState(null);
  const [loading, setLoading] = useState(true);
  const [playing, setPlaying] = useState(true);
  const [taskName, setTaskName] = useState("medium");
  const [error, setError] = useState("");
  const steppingRef = useRef(false);

  async function loadSnapshot() {
    try {
      const response = await fetch(apiUrl("/api/ui/demo"));
      if (!response.ok) {
        throw new Error(`Snapshot request failed with ${response.status}`);
      }
      const data = await response.json();
      setSnapshot(data);
      setTaskName(data.task_name ?? "medium");
      setError("");
    } catch (err) {
      setError(err instanceof Error ? err.message : "Unable to load UI snapshot");
    } finally {
      setLoading(false);
    }
  }

  async function resetDemo(nextTask) {
    setLoading(true);
    try {
      const response = await fetch(apiUrl(`/api/ui/demo/reset?task_name=${encodeURIComponent(nextTask)}`), {
        method: "POST",
      });
      if (!response.ok) {
        throw new Error(`Reset failed with ${response.status}`);
      }
      const data = await response.json();
      setSnapshot(data);
      setTaskName(data.task_name ?? nextTask);
      setError("");
    } catch (err) {
      setError(err instanceof Error ? err.message : "Unable to reset demo");
    } finally {
      setLoading(false);
    }
  }

  async function stepDemo() {
    if (steppingRef.current || loading) {
      return;
    }
    if (snapshot?.done || (snapshot?.step_count ?? 0) >= (snapshot?.max_steps ?? Number.POSITIVE_INFINITY)) {
      setPlaying(false);
      return;
    }

    steppingRef.current = true;
    try {
      const response = await fetch(apiUrl("/api/ui/demo/step"), { method: "POST" });
      if (!response.ok) {
        throw new Error(`Step failed with ${response.status}`);
      }
      const data = await response.json();
      setSnapshot(data);
      if (data.done || (data.step_count ?? 0) >= (data.max_steps ?? Number.POSITIVE_INFINITY)) {
        setPlaying(false);
      }
      setError("");
    } catch (err) {
      setPlaying(false);
      setError(err instanceof Error ? err.message : "Unable to advance demo");
    } finally {
      steppingRef.current = false;
    }
  }

  useEffect(() => {
    loadSnapshot();
  }, []);

  useEffect(() => {
    if (!playing || loading || snapshot?.done || (snapshot?.step_count ?? 0) >= (snapshot?.max_steps ?? Number.POSITIVE_INFINITY)) {
      return undefined;
    }
    const timer = window.setInterval(() => {
      stepDemo();
    }, STEP_INTERVAL_MS);
    return () => window.clearInterval(timer);
  }, [playing, loading, snapshot]);

  return {
    snapshot,
    loading,
    playing,
    taskName,
    error,
    setPlaying,
    resetDemo,
    stepDemo,
  };
}

function GlobeView({ snapshot }) {
  const globeRef = useRef(null);
  const [view, setView] = useState({ lat: 14, lng: 12, altitude: 2.35 });
  const pathStoreRef = useRef([]);
  const [pathData, setPathData] = useState([]);

  useEffect(() => {
    if (!globeRef.current) {
      return;
    }
    const controls = globeRef.current.controls();
    controls.autoRotate = false;
    controls.enablePan = false;
    controls.enableZoom = false;
    controls.minDistance = 220;
    controls.maxDistance = 320;
    controls.minPolarAngle = Math.PI * 0.42;
    controls.maxPolarAngle = Math.PI * 0.58;
    const handleChange = () => {
      const pov = globeRef.current?.pointOfView();
      if (!pov) {
        return;
      }
      const clamped = {
        lat: Math.max(4, Math.min(24, pov.lat)),
        lng: pov.lng,
        altitude: 2.35,
      };
      if (
        Math.abs(clamped.lat - pov.lat) > 0.01 ||
        Math.abs(clamped.altitude - pov.altitude) > 0.01
      ) {
        globeRef.current.pointOfView(clamped, 0);
      }
    };
    controls.addEventListener("change", handleChange);
    return () => {
      controls.removeEventListener("change", handleChange);
    };
  }, []);

  useEffect(() => {
    if (!globeRef.current) {
      return;
    }
    globeRef.current.pointOfView(view, 0);
  }, [view]);

  useEffect(() => {
    const satellites = snapshot?.satellites ?? [];
    const stepCount = snapshot?.step_count ?? 0;

    if (!satellites.length) {
      pathStoreRef.current = [];
      setPathData([]);
      return;
    }

    if (stepCount === 0) {
      const nextPaths = [];
      for (const satellite of satellites) {
        const altitude = 0.11 + Math.min(satellite.altitude_km / 8000, 0.1);
        const orbitPoints = (satellite.route ?? []).map((node) => ({
          lat: node.latitude,
          lng: node.longitude,
          alt: altitude,
        }));
        if (orbitPoints.length > 1) {
          nextPaths.push({
            id: `orbit-${satellite.id}`,
            color: satelliteColor(satellite.last_action),
            points: orbitPoints,
          });
        }
        nextPaths.push({
          id: `trail-${satellite.id}`,
          color: satelliteColor(satellite.last_action),
          points: [
            {
              lat: satellite.latitude,
              lng: satellite.longitude,
              alt: altitude,
            },
          ],
        });
      }
      pathStoreRef.current = nextPaths;
    } else {
      const pathMap = new Map(pathStoreRef.current.map((path) => [path.id, path]));
      for (const satellite of satellites) {
        const orbitId = `orbit-${satellite.id}`;
        const trailId = `trail-${satellite.id}`;
        const color = satelliteColor(satellite.last_action);
        const altitude = 0.11 + Math.min(satellite.altitude_km / 8000, 0.1);
        const nextPoint = {
          lat: satellite.latitude,
          lng: satellite.longitude,
          alt: altitude,
        };

        const orbitPath = pathMap.get(orbitId);
        if (orbitPath) {
          orbitPath.color = color;
        }

        const trailPath = pathMap.get(trailId);
        if (!trailPath) {
          pathMap.set(trailId, {
            id: trailId,
            color,
            points: [nextPoint],
          });
          continue;
        }

        trailPath.color = color;
        const lastPoint = trailPath.points[trailPath.points.length - 1];
        const hasSamePoint =
          lastPoint &&
          Math.abs(lastPoint.lat - nextPoint.lat) < 0.0001 &&
          Math.abs(lastPoint.lng - nextPoint.lng) < 0.0001;

        if (!hasSamePoint) {
          trailPath.points.push(nextPoint);
          if (trailPath.points.length > 160) {
            trailPath.points.shift();
          }
        }
      }
      pathStoreRef.current = [...pathMap.values()];
    }

    const pathMap = new Map(pathStoreRef.current.map((path) => [path.id, path]));
    const activeTransferPaths = new Set();
    for (const transfer of snapshot?.transfers ?? []) {
      if (transfer.kind !== "downlink" && transfer.kind !== "capture") {
        continue;
      }

      const pathId = `${transfer.kind}-${transfer.satellite_id}`;
      activeTransferPaths.add(pathId);
      const points = [
        {
          lat: transfer.from.latitude,
          lng: transfer.from.longitude,
          alt: transfer.kind === "downlink" ? 0.13 : 0.15,
        },
        {
          lat: transfer.to.latitude,
          lng: transfer.to.longitude,
          alt: transfer.kind === "downlink" ? 0.015 : 0.012,
        },
      ];
      const existingPath = pathMap.get(pathId);
      if (existingPath) {
        existingPath.points.splice(0, existingPath.points.length, ...points);
        existingPath.visible = true;
        existingPath.color = transfer.kind === "downlink" ? "#7cf7c9" : "#ffb86b";
      } else {
        pathMap.set(pathId, {
          id: pathId,
          color: transfer.kind === "downlink" ? "#7cf7c9" : "#ffb86b",
          points,
          visible: true,
        });
      }
    }

    for (const path of pathMap.values()) {
      if (
        (String(path.id).startsWith("downlink-") || String(path.id).startsWith("capture-")) &&
        !activeTransferPaths.has(path.id)
      ) {
        path.visible = false;
      }
    }

    pathStoreRef.current = [...pathMap.values()];
    setPathData(pathStoreRef.current.filter((path) => path.visible !== false));
  }, [snapshot]);

  const satellites = (snapshot?.satellites ?? []).map((satellite) => ({
    ...satellite,
    lat: satellite.latitude,
    lng: satellite.longitude,
    altitude: 0.1 + Math.min(satellite.altitude_km / 8000, 0.1),
    type: "satellite",
    color: satelliteColor(satellite.last_action),
    size: 0.45,
    label: `Sat ${satellite.id} · ${satellite.last_action}`,
  }));

  const groundStations = (snapshot?.ground_stations ?? []).map((station) => ({
    ...station,
    lat: station.latitude,
    lng: station.longitude,
    altitude: 0.01,
    type: "station",
    color: "#8cffef",
    size: 0.34,
    label: `${station.name} · ${station.capacity.toFixed(1)} Gbps`,
  }));

  const captureRegions = (snapshot?.capture_regions ?? []).map((region) => ({
    ...region,
    lat: region.latitude,
    lng: region.longitude,
    altitude: 0.009,
    type: "region",
    color: regionColor(region.cloud_cover),
    size: 0.28,
    label: `${region.name} · Cloud ${Math.round(Number(region.cloud_cover) * 100)}%`,
  }));

  const markerNodes = [...groundStations, ...captureRegions, ...satellites];

  return (
    <div className="globe-shell">
      <div className="globe-backdrop" />
      <div className="globe-frame">
        <Globe
          ref={globeRef}
          width={760}
          height={760}
          backgroundColor="rgba(4, 11, 20, 0)"
          backgroundImageUrl={STARFIELD}
          globeImageUrl={EARTH_TEXTURE}
          bumpImageUrl={EARTH_BUMP}
          showAtmosphere
          atmosphereColor="#7ec8ff"
          atmosphereAltitude={0.18}
          animateIn={false}
          waitForGlobeReady={false}
          pathsData={pathData}
          pathPoints="points"
          pathPointLat="lat"
          pathPointLng="lng"
          pathPointAlt="alt"
          pathColor="color"
          pathTransitionDuration={STEP_INTERVAL_MS * 0.82}
          pathStroke={(path) => {
            if (String(path.id).startsWith("orbit-")) {
              return 0.7;
            }
            if (String(path.id).startsWith("trail-")) {
              return 1.55;
            }
            if (String(path.id).startsWith("capture-")) {
              return 1.0;
            }
            return 1.15;
          }}
          pathDashLength={(path) => {
            if (String(path.id).startsWith("orbit-")) {
              return 0;
            }
            if (String(path.id).startsWith("trail-")) {
              return 0;
            }
            if (String(path.id).startsWith("capture-")) {
              return 0.1;
            }
            return 0.22;
          }}
          pathDashGap={(path) => {
            if (String(path.id).startsWith("downlink-")) {
              return 0.14;
            }
            if (String(path.id).startsWith("capture-")) {
              return 0.2;
            }
            return 0;
          }}
          pathDashAnimateTime={0}
          htmlElementsData={markerNodes}
          htmlLat="lat"
          htmlLng="lng"
          htmlAltitude="altitude"
          htmlElement={(marker) => {
            const element = document.createElement("div");
            if (marker.type === "satellite") {
              element.className = "globe-marker satellite";
            } else if (marker.type === "region") {
              element.className = "globe-marker region";
            } else {
              element.className = "globe-marker station";
            }
            element.style.setProperty("--marker-color", marker.color);
            element.style.setProperty("--marker-size", `${marker.size}rem`);
            element.setAttribute("title", marker.label);
            element.innerHTML = `<span></span>`;
            return element;
          }}
        />
      </div>

      <div className="globe-overlay">
        <div>
          <p className="eyebrow">Orbital Flow Console</p>
          <h1>Live tracking across satellites and ground stations.</h1>
        </div>
        <div className="overlay-metrics">
          <div>
            <span>Active satellites</span>
            <strong>{snapshot?.satellites?.length ?? 0}</strong>
          </div>
          <div>
            <span>Ground links</span>
            <strong>{snapshot?.transfers?.filter((item) => item.kind === "downlink").length ?? 0}</strong>
          </div>
        </div>
      </div>
    </div>
  );
}

function satelliteColor(action) {
  if (action === "capture") {
    return "#ffb86b";
  }
  if (action === "downlink") {
    return "#7cf7c9";
  }
  if (action === "maintain") {
    return "#9caaff";
  }
  return "#95a2bb";
}

function regionColor(cloudCover) {
  const cloud = Number(cloudCover);
  if (cloud <= 0.3) {
    return "#88ffd1";
  }
  if (cloud <= 0.6) {
    return "#ffc778";
  }
  return "#ff8f8f";
}

function App() {
  const { snapshot, loading, playing, taskName, error, setPlaying, resetDemo, stepDemo } = useDemoData();

  const weatherSummary = !snapshot?.weather_conditions
    ? []
    : Object.entries(snapshot.weather_conditions).map(([region, cloudCover]) => ({
        region,
        cloudCover,
      }));

  return (
    <div className="app-shell">
      <div className="ambient ambient-a" />
      <div className="ambient ambient-b" />
      <header className="topbar">
        <div className="brand">
          <div className="brand-mark">
            <Orbit size={18} />
          </div>
          <div>
            <p className="eyebrow">OpenEnv Showcase</p>
            <h2>Satellite mission tracking</h2>
          </div>
        </div>
        <div className="task-switcher">
          {TASKS.map((task) => (
            <button
              key={task}
              type="button"
              className={task === taskName ? "task-pill active" : "task-pill"}
              onClick={() => resetDemo(task)}
            >
              {task}
            </button>
          ))}
        </div>
      </header>

      <main className="layout">
        <section className="panel panel-main">
          {loading ? <div className="loading-state">Loading orbital telemetry…</div> : <GlobeView snapshot={snapshot} />}
        </section>

        <section className="panel panel-side">
          <div className="card controls-card">
            <div className="card-head">
              <div>
                <p className="eyebrow">Mission Controls</p>
                <h3>Playback</h3>
              </div>
              <Signal size={18} />
            </div>
            <div className="controls-row">
              <button type="button" className="action-button primary" onClick={() => setPlaying(!playing)}>
                {snapshot?.done ? "Playback complete" : playing ? "Pause loop" : "Resume loop"}
              </button>
              <button
                type="button"
                className="action-button"
                onClick={() => stepDemo()}
                disabled={loading || snapshot?.done || (snapshot?.step_count ?? 0) >= (snapshot?.max_steps ?? 0)}
              >
                Step once
              </button>
            </div>
            <button type="button" className="ghost-button" onClick={() => resetDemo(taskName)}>
              <RefreshCcw size={14} />
              Reinitialize constellation
            </button>
            {error ? <p className="error-text">{error}</p> : null}
          </div>

          <div className="stats-grid">
            <MetricCard icon={<Satellite size={18} />} label="Step" value={`${Math.min(snapshot?.step_count ?? 0, snapshot?.max_steps ?? 0)}/${snapshot?.max_steps ?? 0}`} />
            <MetricCard icon={<Orbit size={18} />} label="Total reward" value={(snapshot?.total_reward ?? 0).toFixed(1)} />
            <MetricCard icon={<Activity size={18} />} label="Last reward" value={(snapshot?.last_reward ?? 0).toFixed(1)} />
            <MetricCard icon={<Gauge size={18} />} label="Tasks done" value={String(snapshot?.metrics?.tasks_completed ?? 0)} />
            <MetricCard icon={<Radio size={18} />} label="Downlink units" value={(snapshot?.metrics?.downlink_units ?? 0).toFixed(1)} />
          </div>

          <div className="card">
            <div className="card-head">
              <div>
                <p className="eyebrow">Fleet Status</p>
                <h3>Satellites</h3>
              </div>
            </div>
            <div className="list">
              {(snapshot?.satellites ?? []).map((satellite) => (
                <motion.div key={satellite.id} className="list-item" layout>
                  <div className="list-title">
                    <span>Sat {satellite.id}</span>
                    <span className={`status-badge ${satellite.last_action}`}>{satellite.last_action}</span>
                  </div>
                  <div className="meter-row">
                    <label>Battery</label>
                    <progress value={satellite.battery} max="100" />
                    <span>{satellite.battery.toFixed(0)}%</span>
                  </div>
                  <div className="meter-row">
                    <label>Storage</label>
                    <progress value={satellite.storage} max="100" />
                    <span>{satellite.storage.toFixed(0)}%</span>
                  </div>
                  <div className="meta-row">
                    <span>{satellite.altitude_km.toFixed(0)} km altitude</span>
                    <span>{satellite.longitude.toFixed(1)}° lon</span>
                  </div>
                </motion.div>
              ))}
            </div>
          </div>

          <div className="card">
            <div className="card-head">
              <div>
                <p className="eyebrow">Network</p>
                <h3>Ground stations & transfers</h3>
              </div>
            </div>
            <div className="list condensed">
              {(snapshot?.ground_stations ?? []).map((station) => (
                <div key={station.id} className="compact-row">
                  <div>
                    <strong>{station.name}</strong>
                    <span>{station.latitude.toFixed(1)}°, {station.longitude.toFixed(1)}°</span>
                  </div>
                  <span>{station.capacity.toFixed(1)} Gbps</span>
                </div>
              ))}
              {(snapshot?.transfers ?? []).map((transfer) => (
                <div key={transfer.id} className="compact-row active-transfer">
                  <div>
                    <strong>{transfer.kind === "downlink" ? "Downlink beam" : "Capture sweep"}</strong>
                    <span>
                      Sat {transfer.satellite_id} · {transfer.amount.toFixed(1)} units
                      {transfer.kind === "downlink" && transfer.ground_station_id !== undefined
                        ? ` · GS ${transfer.ground_station_id}`
                        : ""}
                      {transfer.kind === "capture" && transfer.region ? ` · ${transfer.region}` : ""}
                    </span>
                  </div>
                  <span>{transfer.throughput.toFixed(1)} MB/s</span>
                </div>
              ))}
            </div>
          </div>

          <div className="card">
            <div className="card-head">
              <div>
                <p className="eyebrow">Conditions</p>
                <h3>Weather & queue</h3>
              </div>
            </div>
            <div className="weather-grid">
              {weatherSummary.map((item) => (
                <div key={item.region} className="weather-card">
                  <span>{item.region}</span>
                  <strong>{Math.round(Number(item.cloudCover) * 100)}%</strong>
                  <small>cloud cover</small>
                </div>
              ))}
            </div>
            <div className="task-list">
              {(snapshot?.pending_tasks ?? []).slice(0, 5).map((task) => (
                <div key={task.id} className="task-item">
                  <span>{task.id}</span>
                  <span>
                    {task.type.replaceAll("_", " ")}
                    {task.type === "image_capture" && task.region ? ` · ${task.region}` : ""}
                    {task.type === "data_downlink" && task.station !== undefined ? ` · GS ${task.station}` : ""}
                  </span>
                </div>
              ))}
            </div>
          </div>
        </section>
      </main>
    </div>
  );
}

function MetricCard({ icon, label, value }) {
  return (
    <div className="metric-card">
      <div className="metric-icon">{icon}</div>
      <span>{label}</span>
      <strong>{value}</strong>
    </div>
  );
}

export default App;
