/**
 * Day/Night Terminator Layer for MapLibre
 * Shows a semi-transparent overlay for the night side of Earth
 * Updates in real-time as the Earth rotates
 */
import { useEffect, useRef, useCallback } from "react";

// Calculate the position of the sun based on current time
function getSunPosition(date = new Date()) {
  // Day of year
  const start = new Date(date.getFullYear(), 0, 0);
  const diff = date - start;
  const oneDay = 1000 * 60 * 60 * 24;
  const dayOfYear = Math.floor(diff / oneDay);

  // Solar declination (angle of sun relative to equator)
  // Simplified calculation
  const declination = -23.45 * Math.cos((360 / 365) * (dayOfYear + 10) * (Math.PI / 180));

  // Hour angle (where the sun is longitudinally)
  const hours = date.getUTCHours() + date.getUTCMinutes() / 60 + date.getUTCSeconds() / 3600;
  const longitude = ((12 - hours) / 24) * 360;

  return {
    lat: declination,
    lon: longitude,
  };
}

// Generate night polygon coordinates
// The terminator is the line between day and night
function generateNightPolygon(sunPos, segments = 360) {
  const { lat: sunLat, lon: sunLon } = sunPos;
  const coordinates = [];

  // Generate terminator line points
  for (let i = 0; i <= segments; i++) {
    const angle = (i / segments) * 360;
    const rad = angle * (Math.PI / 180);
    const sunLatRad = sunLat * (Math.PI / 180);

    // Calculate latitude where the terminator crosses this longitude
    // The terminator is where the sun is exactly on the horizon
    const terminatorLat = Math.atan(-Math.cos(rad) / Math.tan(sunLatRad)) * (180 / Math.PI);

    let lng = sunLon + angle;
    if (lng > 180) lng -= 360;
    if (lng < -180) lng += 360;

    coordinates.push([lng, terminatorLat]);
  }

  // Close the polygon by adding polar caps on the night side
  // Determine which pole is in darkness based on sun position
  const nightPoleSide = sunLat > 0 ? -90 : 90;

  // Build the complete night polygon
  const nightPoly = [];

  // First half of terminator (going one direction)
  for (let i = 0; i <= segments / 2; i++) {
    nightPoly.push(coordinates[i]);
  }

  // Go to the dark pole
  const lastPt = coordinates[Math.floor(segments / 2)];
  nightPoly.push([lastPt[0], nightPoleSide]);

  // Follow the dark pole
  const firstPt = coordinates[0];
  nightPoly.push([firstPt[0], nightPoleSide]);

  // Close back to start
  nightPoly.push(coordinates[0]);

  return nightPoly;
}

// Alternative: generate a more accurate night polygon using spherical geometry
function generateNightPolygonAccurate(sunPos, segments = 180) {
  const { lat: sunLat, lon: sunLon } = sunPos;
  const sunLatRad = sunLat * (Math.PI / 180);
  const sunLonRad = sunLon * (Math.PI / 180);

  // Generate points along the terminator circle
  const terminatorPoints = [];
  for (let i = 0; i <= segments; i++) {
    const angle = (i / segments) * 2 * Math.PI;

    // Terminator is a great circle 90 degrees from the sub-solar point
    const lat = Math.asin(
      Math.sin(sunLatRad) * Math.cos(Math.PI / 2) +
      Math.cos(sunLatRad) * Math.sin(Math.PI / 2) * Math.cos(angle)
    );

    const lon = sunLonRad + Math.atan2(
      Math.sin(angle) * Math.sin(Math.PI / 2) * Math.cos(sunLatRad),
      Math.cos(Math.PI / 2) - Math.sin(sunLatRad) * Math.sin(lat)
    );

    let lonDeg = lon * (180 / Math.PI);
    if (lonDeg > 180) lonDeg -= 360;
    if (lonDeg < -180) lonDeg += 360;

    terminatorPoints.push([lonDeg, lat * (180 / Math.PI)]);
  }

  return terminatorPoints;
}

// Build a GeoJSON polygon for the night side
function buildNightGeoJSON(date = new Date()) {
  const sunPos = getSunPosition(date);

  // We'll create a simpler approach: a polygon covering the night hemisphere
  // The night side is opposite to the sun
  const nightLon = sunPos.lon + 180;
  const segments = 180;

  const coordinates = [];

  // Generate the terminator curve
  for (let i = 0; i <= segments; i++) {
    const bearing = (i / segments) * 360;
    const bearingRad = bearing * (Math.PI / 180);
    const sunLatRad = sunPos.lat * (Math.PI / 180);
    const sunLonRad = sunPos.lon * (Math.PI / 180);

    // Point on the terminator (90 degrees from sub-solar point)
    const angularDist = Math.PI / 2; // 90 degrees in radians

    const lat = Math.asin(
      Math.sin(sunLatRad) * Math.cos(angularDist) +
      Math.cos(sunLatRad) * Math.sin(angularDist) * Math.cos(bearingRad)
    );

    const lon = sunLonRad + Math.atan2(
      Math.sin(bearingRad) * Math.sin(angularDist) * Math.cos(sunLatRad),
      Math.cos(angularDist) - Math.sin(sunLatRad) * Math.sin(lat)
    );

    let lonDeg = lon * (180 / Math.PI);
    let latDeg = lat * (180 / Math.PI);

    // Normalize longitude
    while (lonDeg > 180) lonDeg -= 360;
    while (lonDeg < -180) lonDeg += 360;

    coordinates.push([lonDeg, latDeg]);
  }

  // Close the polygon
  coordinates.push(coordinates[0]);

  // Create a polygon that covers the night side
  // We need to determine which side of the terminator is night
  // Night is the hemisphere opposite to the sun
  const nightCenterLon = sunPos.lon > 0 ? sunPos.lon - 180 : sunPos.lon + 180;
  const nightCenterLat = -sunPos.lat;

  // Build polygon ring going around the terminator and back
  // For MapLibre, we need a proper polygon. Let's use a simpler approach:
  // Create a ring that goes around the dark hemisphere

  return {
    type: "Feature",
    properties: {},
    geometry: {
      type: "Polygon",
      coordinates: [coordinates],
    },
  };
}

// Simpler approach: create two polygons, one for each dark cap
function buildNightGeoJSONSimple(date = new Date()) {
  const sunPos = getSunPosition(date);
  const segments = 180;

  // Generate terminator points
  const terminatorPoints = [];
  for (let i = 0; i <= segments; i++) {
    const bearing = (i / segments) * 360;
    const bearingRad = bearing * (Math.PI / 180);
    const sunLatRad = sunPos.lat * (Math.PI / 180);
    const sunLonRad = sunPos.lon * (Math.PI / 180);
    const angularDist = Math.PI / 2;

    const lat = Math.asin(
      Math.sin(sunLatRad) * Math.cos(angularDist) +
      Math.cos(sunLatRad) * Math.sin(angularDist) * Math.cos(bearingRad)
    );

    const lon = sunLonRad + Math.atan2(
      Math.sin(bearingRad) * Math.sin(angularDist) * Math.cos(sunLatRad),
      Math.cos(angularDist) - Math.sin(sunLatRad) * Math.sin(lat)
    );

    let lonDeg = lon * (180 / Math.PI);
    let latDeg = lat * (180 / Math.PI);

    while (lonDeg > 180) lonDeg -= 360;
    while (lonDeg < -180) lonDeg += 360;

    terminatorPoints.push([lonDeg, latDeg]);
  }

  // Sort points by longitude for the polygon
  const sortedPoints = [...terminatorPoints].sort((a, b) => a[0] - b[0]);

  // Determine which pole is dark
  // If sun is in northern hemisphere, south pole area is darker
  // But we want the hemisphere opposite to the sun
  const darkPole = sunPos.lat > 0 ? -89.9 : 89.9;

  // Build night polygon: go along terminator, then to pole, back along -180/180
  const nightRing = [];

  // Add terminator points from west to east
  for (const pt of sortedPoints) {
    nightRing.push(pt);
  }

  // Close by going to the dark pole and back
  const lastPt = sortedPoints[sortedPoints.length - 1];
  const firstPt = sortedPoints[0];

  nightRing.push([180, lastPt[1]]);
  nightRing.push([180, darkPole]);
  nightRing.push([-180, darkPole]);
  nightRing.push([-180, firstPt[1]]);
  nightRing.push(firstPt);

  return {
    type: "FeatureCollection",
    features: [
      {
        type: "Feature",
        properties: { side: "night" },
        geometry: {
          type: "Polygon",
          coordinates: [nightRing],
        },
      },
    ],
  };
}

export default function DayNightTerminator({ map, enabled = true, opacity = 0.3 }) {
  const sourceId = "day-night-source";
  const layerId = "day-night-layer";
  const updateIntervalRef = useRef(null);

  const updateTerminator = useCallback(() => {
    if (!map) return;

    try {
      const source = map.getSource(sourceId);
      if (source) {
        const geojson = buildNightGeoJSONSimple(new Date());
        source.setData(geojson);
      }
    } catch (err) {
      // Ignore errors if map is not ready
    }
  }, [map]);

  useEffect(() => {
    if (!map || !enabled) return;

    // Wait for map to be ready
    const addLayer = () => {
      try {
        // Check if source already exists
        if (map.getSource(sourceId)) {
          map.removeLayer(layerId);
          map.removeSource(sourceId);
        }

        const geojson = buildNightGeoJSONSimple(new Date());

        map.addSource(sourceId, {
          type: "geojson",
          data: geojson,
        });

        map.addLayer({
          id: layerId,
          type: "fill",
          source: sourceId,
          paint: {
            "fill-color": "#000033",
            "fill-opacity": opacity,
          },
        });

        // Update every minute
        updateIntervalRef.current = setInterval(updateTerminator, 60000);
      } catch (err) {
        console.warn("DayNightTerminator: Failed to add layer", err);
      }
    };

    if (map.loaded()) {
      addLayer();
    } else {
      map.once("load", addLayer);
    }

    return () => {
      if (updateIntervalRef.current) {
        clearInterval(updateIntervalRef.current);
      }
      try {
        if (map.getLayer(layerId)) {
          map.removeLayer(layerId);
        }
        if (map.getSource(sourceId)) {
          map.removeSource(sourceId);
        }
      } catch {
        // Map might be destroyed
      }
    };
  }, [map, enabled, opacity, updateTerminator]);

  // Update opacity when it changes
  useEffect(() => {
    if (!map || !enabled) return;
    try {
      if (map.getLayer(layerId)) {
        map.setPaintProperty(layerId, "fill-opacity", opacity);
      }
    } catch {
      // Ignore
    }
  }, [map, enabled, opacity]);

  return null; // This component doesn't render anything visible
}

// Export helper for use in other components
export { getSunPosition, buildNightGeoJSONSimple };
