import { HomePageClient } from "@/components/HomePageClient";
import { PARKS } from "@/lib/parks";
import { openDb, getDateRange } from "@/lib/db";
import { getTodayLocal, isWithinOperatingWindow, getOperatingStatus } from "@/lib/env";
import { fetchLiveRides } from "@/lib/scrapers/queuetimes";
import { fetchToday } from "@/lib/scrapers/sixflags";
import { QUEUE_TIMES_IDS } from "@/lib/queue-times-map";
import { getCoasterSet, hasCoasterData } from "@/lib/coaster-data";
import type { DayData } from "@/lib/db";

interface PageProps {
  searchParams: Promise<{ week?: string }>;
}

function getWeekStart(param: string | undefined): string {
  if (param && /^\d{4}-\d{2}-\d{2}$/.test(param)) {
    const d = new Date(param + "T00:00:00");
    if (!isNaN(d.getTime())) {
      d.setDate(d.getDate() - d.getDay());
      return d.toISOString().slice(0, 10);
    }
  }
  const todayIso = getTodayLocal();
  const d = new Date(todayIso + "T00:00:00");
  d.setDate(d.getDate() - d.getDay());
  return d.toISOString().slice(0, 10);
}

function getWeekDates(sundayIso: string): string[] {
  return Array.from({ length: 7 }, (_, i) => {
    const d = new Date(sundayIso + "T00:00:00");
    d.setDate(d.getDate() + i);
    return d.toISOString().slice(0, 10);
  });
}

function getCurrentWeekStart(): string {
  const todayIso = getTodayLocal();
  const d = new Date(todayIso + "T00:00:00");
  d.setDate(d.getDate() - d.getDay());
  return d.toISOString().slice(0, 10);
}

export default async function HomePage({ searchParams }: PageProps) {
  const params = await searchParams;
  const weekStart = getWeekStart(params.week);
  const weekDates = getWeekDates(weekStart);
  const endDate = weekDates[6];
  const today = getTodayLocal();
  const isCurrentWeek = weekStart === getCurrentWeekStart();

  const db = openDb();
  const data = getDateRange(db, weekStart, endDate);

  // Merge live today data from the Six Flags API (dateless endpoint, 5-min ISR cache).
  // This ensures weather delays, early closures, and hour changes surface within 5 minutes
  // without waiting for the next scheduled scrape. Only fetched when viewing the current week.
  if (weekDates.includes(today)) {
    const todayResults = await Promise.all(
      PARKS.map(async (p) => {
        const live = await fetchToday(p.apiId, 300); // 5-min ISR cache
        return live ? { parkId: p.id, live } : null;
      })
    );
    for (const result of todayResults) {
      if (!result) continue;
      const { parkId, live } = result;
      if (!data[parkId]) data[parkId] = {};
      data[parkId][today] = {
        isOpen: live.isOpen,
        hoursLabel: live.hoursLabel ?? null,
        specialType: live.specialType ?? null,
      } satisfies DayData;
    }
  }

  db.close();

  const scrapedCount = Object.values(data).reduce(
    (sum, parkData) => sum + Object.keys(parkData).length,
    0
  );

  const coasterDataAvailable = hasCoasterData();

  let rideCounts: Record<string, number> = {};
  let coasterCounts: Record<string, number> = {};
  let closingParkIds: string[] = [];
  let openParkIds: string[] = [];
  let weatherDelayParkIds: string[] = [];
  if (weekDates.includes(today)) {
    // Parks within operating hours right now (for open dot — independent of ride counts)
    const openTodayParks = PARKS.filter((p) => {
      const dayData = data[p.id]?.[today];
      if (!dayData?.isOpen || !dayData.hoursLabel) return false;
      return isWithinOperatingWindow(dayData.hoursLabel, p.timezone);
    });
    openParkIds = openTodayParks.map((p) => p.id);
    closingParkIds = openTodayParks
      .filter((p) => {
        const dayData = data[p.id]?.[today];
        return dayData?.hoursLabel
          ? getOperatingStatus(dayData.hoursLabel, p.timezone) === "closing"
          : false;
      })
      .map((p) => p.id);
    // Only fetch ride counts for parks that have queue-times coverage
    const trackedParks = openTodayParks.filter((p) => QUEUE_TIMES_IDS[p.id]);
    const results = await Promise.all(
      trackedParks.map(async (p) => {
        const coasterSet = getCoasterSet(p.id);
        const result = await fetchLiveRides(QUEUE_TIMES_IDS[p.id], coasterSet, 300);
        const rideCount = result ? result.rides.filter((r) => r.isOpen).length : null;
        const coasterCount = result ? result.rides.filter((r) => r.isOpen && r.isCoaster).length : 0;
        return { id: p.id, rideCount, coasterCount };
      })
    );
    // Parks with queue-times coverage but 0 open rides = likely weather delay
    weatherDelayParkIds = results
      .filter(({ rideCount }) => rideCount === 0)
      .map(({ id }) => id);
    rideCounts = Object.fromEntries(
      results.filter(({ rideCount }) => rideCount != null && rideCount > 0).map(({ id, rideCount }) => [id, rideCount!])
    );
    coasterCounts = Object.fromEntries(
      results.filter(({ coasterCount }) => coasterCount > 0).map(({ id, coasterCount }) => [id, coasterCount])
    );
  }

  return (
    <HomePageClient
      weekStart={weekStart}
      weekDates={weekDates}
      today={today}
      isCurrentWeek={isCurrentWeek}
      data={data}
      rideCounts={rideCounts}
      coasterCounts={coasterCounts}
      openParkIds={openParkIds}
      closingParkIds={closingParkIds}
      weatherDelayParkIds={weatherDelayParkIds}
      hasCoasterData={coasterDataAvailable}
      scrapedCount={scrapedCount}
    />
  );
}
