import { describe, it, expect } from 'vitest';
import { computeNightPolygon } from '@/utils/solarTerminator';

/** Extract polygon ring from result (type-narrowing helper) */
function getRing(result: GeoJSON.FeatureCollection): number[][] {
  const geom = result.features[0].geometry;
  if (geom.type !== 'Polygon') throw new Error('Expected Polygon geometry');
  return geom.coordinates[0];
}

describe('computeNightPolygon', () => {
  // ─── Structure validation ────────────────────────────────────────────────

  it('returns a valid GeoJSON FeatureCollection', () => {
    const result = computeNightPolygon();
    expect(result.type).toBe('FeatureCollection');
    expect(result.features).toHaveLength(1);
    expect(result.features[0].type).toBe('Feature');
    expect(result.features[0].geometry.type).toBe('Polygon');
  });

  it('polygon has at least 360 vertices (one per degree of longitude)', () => {
    const ring = getRing(computeNightPolygon());
    // 361 terminator points + 2 closing corners + 1 ring-close = ≥364
    expect(ring.length).toBeGreaterThanOrEqual(364);
  });

  it('polygon ring is closed (first and last points match)', () => {
    const ring = getRing(computeNightPolygon());
    expect(ring[ring.length - 1]).toEqual(ring[0]);
  });

  // ─── Coordinate bounds ───────────────────────────────────────────────────

  it('all coordinates are within valid lat/lng bounds', () => {
    const ring = getRing(computeNightPolygon());
    for (const [lng, lat] of ring) {
      expect(lng).toBeGreaterThanOrEqual(-180);
      expect(lng).toBeLessThanOrEqual(180);
      expect(lat).toBeGreaterThanOrEqual(-85);
      expect(lat).toBeLessThanOrEqual(85);
    }
  });

  // ─── Deterministic for same input ────────────────────────────────────────

  it('returns identical result for the same date', () => {
    const date = new Date('2024-06-21T12:00:00Z');
    const result1 = computeNightPolygon(date);
    const result2 = computeNightPolygon(date);
    expect(result1).toEqual(result2);
  });

  // ─── Seasonal behavior ──────────────────────────────────────────────────

  it('equinox produces roughly symmetric polygon', () => {
    const equinox = new Date('2024-03-20T12:00:00Z');
    const ring = getRing(computeNightPolygon(equinox));
    const lats = ring.map(([, lat]: number[]) => lat);
    const maxLat = Math.max(...lats);
    const minLat = Math.min(...lats);
    expect(maxLat).toBeGreaterThan(50);
    expect(minLat).toBeLessThan(-50);
  });

  it('summer solstice shifts night polygon southward', () => {
    const summer = new Date('2024-06-21T00:00:00Z');
    const ring = getRing(computeNightPolygon(summer));
    const terminatorLats = ring
      .filter(([lng]: number[]) => lng >= -180 && lng <= 180)
      .slice(0, 361)
      .map(([, lat]: number[]) => lat);
    const avgLat = terminatorLats.reduce((a: number, b: number) => a + b, 0) / terminatorLats.length;
    expect(avgLat).toBeLessThan(15);
  });

  // ─── Different times produce different results ──────────────────────────

  it('produces different polygons for different times of day', () => {
    const morning = new Date('2024-06-21T06:00:00Z');
    const evening = new Date('2024-06-21T18:00:00Z');
    const ringM = getRing(computeNightPolygon(morning));
    const ringE = getRing(computeNightPolygon(evening));
    expect(ringM[0]).not.toEqual(ringE[0]);
  });
});
