// night-sky-view.jsx
// AGENT_TARGET: night-sky-view — geolocation-based planet visibility for tonight

"use strict";

const NS_PLANET_DATA = [
  {
    id: "mercury",
    name: "Mercury",
    emoji: "☿",
    color: "#b0a090",
    L0: 252.25,
    dL: 4.09234,
  },
  {
    id: "venus",
    name: "Venus",
    emoji: "♀",
    color: "#f0d080",
    L0: 181.98,
    dL: 1.60213,
  },
  {
    id: "mars",
    name: "Mars",
    emoji: "♂",
    color: "#e06040",
    L0: 355.43,
    dL: 0.52403,
  },
  {
    id: "jupiter",
    name: "Jupiter",
    emoji: "♃",
    color: "#d0b080",
    L0: 34.35,
    dL: 0.08312,
  },
  {
    id: "saturn",
    name: "Saturn",
    emoji: "♄",
    color: "#e0c878",
    L0: 50.08,
    dL: 0.03349,
  },
  {
    id: "uranus",
    name: "Uranus",
    emoji: "⛢",
    color: "#80d8e8",
    L0: 314.05,
    dL: 0.01173,
  },
  {
    id: "neptune",
    name: "Neptune",
    emoji: "♆",
    color: "#4080e8",
    L0: 304.35,
    dL: 0.006,
  },
];

// Heliocentric semi-major axes (AU) for geocentric calc
const NS_PLANET_A = {
  mercury: 0.387,
  venus: 0.723,
  mars: 1.524,
  jupiter: 5.203,
  saturn: 9.537,
  uranus: 19.19,
  neptune: 30.07,
};

function nsJulianDay(date) {
  // AGENT_TARGET: night-sky-view — date to Julian Day Number
  return date.getTime() / 86400000 + 2440587.5;
}

function nsSunLon(jd) {
  const d = jd - 2451545.0;
  const g = (((357.5291 + 0.98560028 * d) % 360) + 360) % 360;
  const gRad = (g * Math.PI) / 180;
  return (
    (((280.4665 +
      0.9856235 * d +
      1.9148 * Math.sin(gRad) +
      0.02 * Math.sin(2 * gRad)) %
      360) +
      360) %
    360
  );
}

function nsPlanetLon(jd, planet) {
  // Rough heliocentric longitude via mean motion
  const d = jd - 2451545.0;
  return (((planet.L0 + planet.dL * d) % 360) + 360) % 360;
}

function nsGeocentricLon(jd, planet) {
  // Convert heliocentric → geocentric via simplified vector sum
  const sunLon = nsSunLon(jd);
  const pLon = nsPlanetLon(jd, planet);
  const a = NS_PLANET_A[planet.id] || 1;
  // Vector sum of earth-sun and sun-planet vectors
  const sunRad = (sunLon * Math.PI) / 180;
  const pRad = (pLon * Math.PI) / 180;
  const ex = Math.cos(sunRad + Math.PI);
  const ey = Math.sin(sunRad + Math.PI);
  const px = a * Math.cos(pRad);
  const py = a * Math.sin(pRad);
  const gx = px + ex;
  const gy = py + ey;
  return ((Math.atan2(gy, gx) * 180) / Math.PI + 360) % 360;
}

function nsElongation(jd, planet) {
  const sunLon = nsSunLon(jd);
  const geoLon = nsGeocentricLon(jd, planet);
  let e = geoLon - sunLon;
  if (e > 180) e -= 360;
  if (e < -180) e += 360;
  return e;
}

function nsEclToEq(lambda, beta, obliquity) {
  // Ecliptic → equatorial
  const lRad = (lambda * Math.PI) / 180;
  const bRad = (beta * Math.PI) / 180;
  const eRad = (obliquity * Math.PI) / 180;
  const ra =
    (Math.atan2(
      Math.sin(lRad) * Math.cos(eRad) - Math.tan(bRad) * Math.sin(eRad),
      Math.cos(lRad),
    ) *
      180) /
    Math.PI;
  const dec =
    (Math.asin(
      Math.sin(bRad) * Math.cos(eRad) +
        Math.cos(bRad) * Math.sin(eRad) * Math.sin(lRad),
    ) *
      180) /
    Math.PI;
  return { ra: (ra + 360) % 360, dec };
}

function nsLocalSiderealTime(jd, lonDeg) {
  // Greenwich Mean Sidereal Time → Local
  const T = (jd - 2451545.0) / 36525;
  const gmst =
    280.46061837 + 360.98564736629 * (jd - 2451545.0) + 0.000387933 * T * T;
  return (((gmst + lonDeg) % 360) + 360) % 360;
}

function nsAzAlt(jd, geoLon, latDeg, lonDeg) {
  // AGENT_TARGET: night-sky-view — compute azimuth and altitude from geocentric lon
  const obliquity = 23.439; // degrees, approx
  const { ra, dec } = nsEclToEq(geoLon, 0, obliquity);
  const lst = nsLocalSiderealTime(jd, lonDeg);
  const ha = (((lst - ra) % 360) + 360) % 360; // hour angle in degrees
  const haRad = (ha * Math.PI) / 180;
  const latRad = (latDeg * Math.PI) / 180;
  const decRad = (dec * Math.PI) / 180;

  const sinAlt =
    Math.sin(latRad) * Math.sin(decRad) +
    Math.cos(latRad) * Math.cos(decRad) * Math.cos(haRad);
  const alt = (Math.asin(Math.max(-1, Math.min(1, sinAlt))) * 180) / Math.PI;

  const cosAz =
    (Math.sin(decRad) - Math.sin(latRad) * sinAlt) /
    (Math.cos(latRad) * Math.cos((alt * Math.PI) / 180));
  let az = (Math.acos(Math.max(-1, Math.min(1, cosAz))) * 180) / Math.PI;
  if (Math.sin(haRad) > 0) az = 360 - az;

  return { az, alt };
}

function nsCompassLabel(az) {
  const dirs = ["N", "NE", "E", "SE", "S", "SW", "W", "NW"];
  return dirs[Math.round(az / 45) % 8];
}

function nsAltLabel(alt) {
  if (alt > 60) return "high up";
  if (alt > 30) return "halfway up";
  if (alt > 10) return "low";
  return "near horizon";
}

function NightSkyView({ onClose }) {
  const [state, setState] = React.useState("idle"); // idle | loading | ready | denied
  const [location, setLocation] = React.useState(null);
  const [planets, setPlanets] = React.useState([]);
  const [now] = React.useState(() => new Date());

  const requestLocation = () => {
    if (!navigator.geolocation) {
      setState("denied");
      return;
    }
    setState("loading");
    navigator.geolocation.getCurrentPosition(
      (pos) => {
        const lat = pos.coords.latitude;
        const lon = pos.coords.longitude;
        setLocation({ lat, lon });
        const jd = nsJulianDay(now);
        const results = NS_PLANET_DATA.map((planet) => {
          const elong = nsElongation(jd, planet);
          const geoLon = nsGeocentricLon(jd, planet);
          const { az, alt } = nsAzAlt(jd, geoLon, lat, lon);
          const visible = alt > 5; // above 5° horizon threshold
          const isEvening = elong > 0;
          return { ...planet, elong, az, alt, visible, isEvening };
        });
        setPlanets(results);
        setState("ready");
      },
      () => {
        // On denial, show approximate view using lat 40N lon -75W (eastern US default)
        const lat = 40,
          lon = -75;
        const jd = nsJulianDay(now);
        const results = NS_PLANET_DATA.map((planet) => {
          const elong = nsElongation(jd, planet);
          const geoLon = nsGeocentricLon(jd, planet);
          const { az, alt } = nsAzAlt(jd, geoLon, lat, lon);
          const visible = alt > 5;
          const isEvening = elong > 0;
          return { ...planet, elong, az, alt, visible, isEvening };
        });
        setPlanets(results);
        setLocation({ lat, lon, approximate: true });
        setState("ready");
      },
      { timeout: 8000, maximumAge: 600000 },
    );
  };

  useEscapeHandler(onClose);

  const visiblePlanets = planets.filter((p) => p.visible);
  const hiddenPlanets = planets.filter((p) => !p.visible);

  const dateLabel = now.toLocaleDateString(undefined, {
    month: "long",
    day: "numeric",
  });
  const timeLabel = now.toLocaleTimeString(undefined, {
    hour: "2-digit",
    minute: "2-digit",
  });

  return (
    <div
      className="night-sky-overlay"
      role="dialog"
      aria-label="Night sky stargazing view"
    >
      <div className="night-sky-panel">
        <button
          className="night-sky-close"
          onClick={onClose}
          aria-label="Close night sky view"
        >
          ✕
        </button>
        <div className="night-sky-title-row">
          <span className="night-sky-icon">🌌</span>
          <div>
            <div className="night-sky-title">Tonight's Sky</div>
            <div className="night-sky-subtitle">
              {dateLabel} · {timeLabel}
            </div>
          </div>
        </div>

        {state === "idle" && (
          <div className="night-sky-prompt">
            <p className="night-sky-prompt-text">
              See which planets are in <em>your</em> backyard sky right now!
            </p>
            <button className="night-sky-locate-btn" onClick={requestLocation}>
              📍 Use my location
            </button>
          </div>
        )}

        {state === "loading" && (
          <div className="night-sky-loading">Finding your sky…</div>
        )}

        {state === "ready" && (
          <>
            {location && location.approximate && (
              <div className="night-sky-approx-note">
                Showing approximate sky (location unavailable)
              </div>
            )}

            {/* Sky compass */}
            {visiblePlanets.length > 0 ? (
              <>
                <div
                  className="night-sky-compass"
                  aria-label="Sky compass showing planet directions"
                >
                  <svg
                    viewBox="-110 -110 220 130"
                    className="night-sky-compass-svg"
                  >
                    {/* Horizon arc */}
                    <path
                      d="M -100 0 A 100 100 0 0 1 100 0"
                      fill="none"
                      stroke="rgba(120,160,255,0.2)"
                      strokeWidth="1.5"
                    />
                    {/* Cardinal labels */}
                    <text x="-106" y="5" className="ns-compass-label">
                      W
                    </text>
                    <text x="100" y="5" className="ns-compass-label">
                      E
                    </text>
                    <text x="-5" y="-100" className="ns-compass-label">
                      S
                    </text>
                    {/* Zenith dot */}
                    <circle
                      cx="0"
                      cy="-100"
                      r="2"
                      fill="rgba(255,255,255,0.15)"
                    />
                    {/* Grid lines */}
                    <line
                      x1="-100"
                      y1="0"
                      x2="100"
                      y2="0"
                      stroke="rgba(120,160,255,0.12)"
                      strokeWidth="1"
                    />
                    <path
                      d="M -70 -71 A 100 100 0 0 1 70 -71"
                      fill="none"
                      stroke="rgba(120,160,255,0.1)"
                      strokeWidth="0.8"
                      strokeDasharray="4 4"
                    />
                    {/* Planet dots */}
                    {visiblePlanets.map((planet) => {
                      // Map azimuth (0=N, 90=E, 180=S, 270=W) to compass SVG x
                      // We show S at top (az=180→x=0), E at right (az=90→x=100), W at left (az=270→x=-100)
                      const azRel = planet.az - 180; // shift so south is center
                      const xFrac = Math.sin((azRel * Math.PI) / 180);
                      const yFrac = Math.cos((planet.alt * Math.PI) / 180);
                      const px = xFrac * 100;
                      const py = -(planet.alt / 90) * 100;
                      return (
                        <g key={planet.id}>
                          <circle
                            cx={px}
                            cy={py}
                            r="7"
                            fill={planet.color}
                            opacity="0.85"
                          />
                          <text
                            x={px}
                            y={py + 4}
                            textAnchor="middle"
                            className="ns-planet-dot-label"
                          >
                            {planet.emoji}
                          </text>
                          <text
                            x={px}
                            y={py + 14}
                            textAnchor="middle"
                            className="ns-planet-dot-name"
                          >
                            {planet.name}
                          </text>
                        </g>
                      );
                    })}
                  </svg>
                  <div className="ns-compass-horizon-label">
                    Horizon (facing South)
                  </div>
                </div>

                <div
                  className="night-sky-planet-list"
                  aria-label="Visible planets"
                >
                  <div className="night-sky-reading-guide">
                    <span>
                      <strong>Direction</strong> Turn toward N, E, S, or W.
                    </span>
                    <span>
                      <strong>Height</strong> Low is near the horizon; high up is overhead.
                    </span>
                    <span>
                      <strong>Sky time</strong> Evening planets follow sunset; morning planets rise before sunrise.
                    </span>
                  </div>
                  {visiblePlanets.map((planet) => (
                    <div key={planet.id} className="ns-planet-row">
                      <span
                        className="ns-planet-emoji"
                        style={{ color: planet.color }}
                      >
                        {planet.emoji}
                      </span>
                      <div className="ns-planet-info">
                        <strong>{planet.name}</strong>
                        <span>
                          {nsCompassLabel(planet.az)} · {nsAltLabel(planet.alt)}{" "}
                          · {planet.isEvening ? "evening sky" : "morning sky"}
                        </span>
                      </div>
                      <div
                        className="ns-planet-arrow"
                        style={{ transform: `rotate(${planet.az}deg)` }}
                        aria-hidden="true"
                      >
                        ↑
                      </div>
                    </div>
                  ))}
                </div>
              </>
            ) : (
              <div className="night-sky-none">
                No bright planets are above the horizon right now — try again
                after sunset or before sunrise!
              </div>
            )}

            {hiddenPlanets.length > 0 && (
              <div className="night-sky-hidden">
                <div className="night-sky-hidden-label">
                  Not visible tonight:
                </div>
                <div className="night-sky-hidden-list">
                  {hiddenPlanets.map((p) => (
                    <span
                      key={p.id}
                      className="night-sky-hidden-chip"
                      style={{ borderColor: p.color }}
                    >
                      {p.emoji} {p.name}
                    </span>
                  ))}
                </div>
              </div>
            )}

            <button className="night-sky-refresh" onClick={requestLocation}>
              ↻ Refresh
            </button>
          </>
        )}

        {state === "denied" && (
          <div className="night-sky-denied">
            Your device does not support location. Try on a phone or tablet!
          </div>
        )}
      </div>
    </div>
  );
}
