import { useState, useEffect } from "react";
import { redirect, useFetcher } from "react-router";
import { auth } from "~/lib/auth.server";
import { authClient } from "~/lib/auth-client";
import type { Route } from "./+types/new";

import { logger } from "~/lib/logger";
import { createLeague, setCurrentSeason } from "~/models/league";
import { createCommissioner } from "~/models/commissioner";
import { createSeason } from "~/models/season";
import { linkMultipleSportsToSeason } from "~/models/season-sport";
import { createManyTeams } from "~/models/team";
import { findActiveSeasonTemplates, findSeasonTemplateWithSportsSeasons } from "~/models/season-template";
import { findDraftableSportsSeasons } from "~/models/sports-season";
import { findUserById, updateUser } from "~/models/user";
import { generateUniqueTeamNames, prependOwnerToTeamName } from "~/utils/team-names";
import { format } from "date-fns";
import { CalendarClock, Check, Info, Star, Swords, Trophy } from "lucide-react";
import { Badge } from "~/components/ui/badge";
import { Button } from "~/components/ui/button";
import { Card, CardContent } from "~/components/ui/card";
import { Input } from "~/components/ui/input";
import { Label } from "~/components/ui/label";
import { Checkbox } from "~/components/ui/checkbox";
import { cn } from "~/lib/utils";
import { parseDraftSpeed, formatTime12h } from "~/lib/draft-timer";
import { TIMEZONE_OPTIONS } from "~/components/league/TimezoneSelect";
import { useLocalTimezone } from "~/hooks/useLocalTimezone";
import { saveWizardState, buildFormDataFromSnapshot } from "~/lib/wizard-state";
import { type ScoringRules, DEFAULT_SCORING_RULES } from "~/lib/scoring-types";
import { SportIcon } from "~/components/league/SportIcon";
import { TimerModeSelector } from "~/components/league/TimerModeSelector";
import { DraftSpeedPicker, formatDraftSpeed, isSlowPreset } from "~/components/league/DraftSpeedPicker";
import { OvernightPauseSettings } from "~/components/league/OvernightPauseSettings";
import { ScoringPresetPicker, PLACEMENT_LABELS } from "~/components/league/ScoringPresetPicker";
import { WizardStepper } from "~/components/league/WizardStepper";
import { ReviewSection } from "~/components/league/ReviewSection";
import { StepperInput } from "~/components/league/StepperInput";
import { WizardAuthForm } from "~/components/league/WizardAuthForm";
import {
  applyBracktSportsForSeason,
  seedOrRepairBracktTemplate,
} from "~/services/brackt.server";

// ─── Types ────────────────────────────────────────────────────────────────────

type SportSeason = {
  id: string;
  name: string;
  year: number;
  scoringType: string;
  fantasySeasonId: string | null;
  sport: { id: string; name: string; type: string; slug: string; iconUrl: string | null };
  participantCount: number;
};

type Template = {
  id: string;
  name: string;
  year: number;
  description: string | null;
  sportsSeasonIds: string[];
};

const BRACKT_SPORT_TOOLTIP =
  "Draft managers from your own league. They score based on how their teams perform across the other sports.";

function isBracktSport(sport: { slug: string }) {
  return sport.slug === "brackt";
}

function BracktInfoIcon() {
  return (
    <span
      title={BRACKT_SPORT_TOOLTIP}
      aria-label={BRACKT_SPORT_TOOLTIP}
      className="inline-flex text-muted-foreground"
    >
      <Info className="h-3.5 w-3.5" />
    </span>
  );
}

function defaultRounds(count: number): number {
  if (count <= 0) return 3;
  const pct = count >= 20 ? 1.25 : 1.33;
  return Math.max(count + 3, Math.ceil(count * pct));
}

// ─── Meta ─────────────────────────────────────────────────────────────────────

export function meta(): Route.MetaDescriptors {
  return [{ title: "New League - Brackt" }];
}

// ─── Loader ───────────────────────────────────────────────────────────────────

export async function loader(args: Route.LoaderArgs) {
  const session = await auth.api.getSession({ headers: args.request.headers });
  const userId = session?.user.id ?? null;

  await seedOrRepairBracktTemplate();

  const templates = await findActiveSeasonTemplates();
  const allSportsSeasons = await findDraftableSportsSeasons();

  const templatesWithSports = await Promise.all(
    templates.map(async (template) => {
      const t = await findSeasonTemplateWithSportsSeasons(template.id);
      return {
        ...template,
        sportsSeasonIds: t?.seasonTemplateSports?.map(
          (ts: { sportsSeason: { id: string } }) => ts.sportsSeason.id
        ) ?? [],
      };
    })
  );

  const commishTimezone = userId
    ? ((await findUserById(userId))?.timezone ?? null)
    : null;

  return {
    templates: templatesWithSports as Template[],
    allSportsSeasons: allSportsSeasons as SportSeason[],
    commishTimezone,
    isAuthenticated: userId !== null,
  };
}

// ─── Action ───────────────────────────────────────────────────────────────────

export async function action(args: Route.ActionArgs) {
  const session = await auth.api.getSession({ headers: args.request.headers });
  const userId = session?.user.id ?? null;
  const { request } = args;

  if (!userId) {
    throw new Response("Unauthorized", { status: 401 });
  }

  const formData = await request.formData();
  const name = formData.get("name");
  const teamCount = formData.get("teamCount");
  const templateId = formData.get("templateId");
  const draftRounds = formData.get("draftRounds");
  const draftDateTime = formData.get("draftDateTime");
  const autoStartDraft = formData.get("autoStartDraft") === "on" && !!(typeof draftDateTime === "string" && draftDateTime);
  const draftSpeed = formData.get("draftSpeed");
  const draftTimerMode = (formData.get("draftTimerMode") as "chess_clock" | "standard") || "chess_clock";

  const { draftInitialTime: draftInitialTimeNum, draftIncrementTime: draftIncrementTimeNum } =
    parseDraftSpeed(draftSpeed as string | null, draftTimerMode);

  if (typeof name !== "string" || !name.trim()) {
    return { error: "League name is required" };
  }

  if (typeof teamCount !== "string") {
    return { error: "Number of teams is required" };
  }

  const teamCountNum = parseInt(teamCount, 10);
  if (isNaN(teamCountNum) || teamCountNum < 6 || teamCountNum > 16) {
    return { error: "Number of teams must be between 6 and 16" };
  }

  const draftRoundsNum = typeof draftRounds === "string" ? parseInt(draftRounds, 10) : 20;
  if (isNaN(draftRoundsNum) || draftRoundsNum < 1 || draftRoundsNum > 50) {
    return { error: "Draft rounds must be between 1 and 50" };
  }

  const scoringFields = [
    "pointsFor1st", "pointsFor2nd", "pointsFor3rd", "pointsFor4th",
    "pointsFor5th", "pointsFor6th", "pointsFor7th", "pointsFor8th"
  ];

  const scoringRules: Record<string, number> = {};
  for (const field of scoringFields) {
    const value = formData.get(field);
    if (typeof value === "string") {
      const points = parseInt(value, 10);
      if (!isNaN(points)) {
        if (points < 0 || points > 1000) {
          return { error: `${field} must be between 0 and 1000 points` };
        }
        scoringRules[field] = points;
      }
    }
  }

  const rawPauseMode = formData.get("overnightPauseMode") as string | null;
  const overnightPauseMode: "none" | "league" | "per_user" =
    rawPauseMode === "league" || rawPauseMode === "per_user" ? rawPauseMode : "none";
  const overnightPauseStart = overnightPauseMode !== "none"
    ? (formData.get("overnightPauseStart") as string | null) ?? "23:00"
    : null;
  const overnightPauseEnd = overnightPauseMode !== "none"
    ? (formData.get("overnightPauseEnd") as string | null) ?? "07:00"
    : null;
  const overnightPauseTimezone = overnightPauseMode !== "none"
    ? (formData.get("overnightPauseTimezone") as string | null) ?? null
    : null;

  try {
    const league = await createLeague({
      name: name.trim(),
      createdBy: userId,
    });

    await createCommissioner({
      leagueId: league.id,
      userId: userId,
    });

    const currentYear = new Date().getFullYear();
    const season = await createSeason({
      leagueId: league.id,
      year: currentYear,
      status: "pre_draft",
      templateId: typeof templateId === "string" && templateId !== "" && templateId !== "customize"
        ? templateId
        : null,
      draftRounds: draftRoundsNum,
      draftDateTime: typeof draftDateTime === "string" && draftDateTime ? new Date(draftDateTime) : null,
      autoStartDraft,
      draftInitialTime: draftInitialTimeNum,
      draftIncrementTime: draftIncrementTimeNum,
      draftTimerMode,
      overnightPauseMode,
      overnightPauseStart,
      overnightPauseEnd,
      overnightPauseTimezone,
      ...scoringRules,
    });

    await setCurrentSeason(league.id, season.id);

    const selectedSports = formData.getAll("sportsSeasons").map(String);

    if (selectedSports.length > draftRoundsNum) {
      return {
        error: `You need at least ${selectedSports.length} draft rounds for the ${selectedSports.length} sports selected. Currently set to ${draftRoundsNum} rounds.`
      };
    }

    const joinAsPlayer = formData.get("joinAsPlayer") === "on";
    const teamNames = generateUniqueTeamNames(teamCountNum);
    const creator = await findUserById(userId);
    const creatorUsername = creator?.username ?? creator?.displayName;
    const teams = teamNames.map((teamName, i) => ({
      seasonId: season.id,
      name: joinAsPlayer && i === 0 && creatorUsername
        ? prependOwnerToTeamName(teamName, creatorUsername)
        : teamName,
      ownerId: joinAsPlayer && i === 0 ? userId : null,
    }));

    await createManyTeams(teams);

    const sportsToLink = await applyBracktSportsForSeason(season.id, selectedSports);
    if (sportsToLink.length > 0) {
      await linkMultipleSportsToSeason(
        sportsToLink.map((id) => ({
          seasonId: season.id,
          sportsSeasonId: id,
        }))
      );
    }

    const userTimezone = formData.get("userTimezone");
    if (typeof userTimezone === "string" && userTimezone) {
      await updateUser(userId, { timezone: userTimezone });
    }

    return redirect(`/leagues/${league.id}`);
  } catch (error) {
    logger.error("Error creating league:", error);
    return { error: "Failed to create league. Please try again." };
  }
}

// ─── Step 1: League Basics ────────────────────────────────────────────────────

function Step1LeagueBasics({
  leagueName,
  setLeagueName,
  teamCount,
  setTeamCount,
  joinAsPlayer,
  setJoinAsPlayer,
  error,
  onNext,
}: {
  leagueName: string;
  setLeagueName: (v: string) => void;
  teamCount: number;
  setTeamCount: (v: number) => void;
  joinAsPlayer: boolean;
  setJoinAsPlayer: (v: boolean) => void;
  error: string | null;
  onNext: () => void;
}) {
  const [nameTouched, setNameTouched] = useState(false);
  const nameInvalid = nameTouched && !leagueName.trim();

  return (
    <div className="space-y-6">
      <div>
        <h2 className="text-xl font-semibold flex items-center gap-2">
          <span className="w-7 h-7 rounded-full bg-primary text-primary-foreground flex items-center justify-center text-sm font-bold shrink-0">1</span>
          League Basics
        </h2>
      </div>

      <div className="space-y-2">
        <Label htmlFor="name">League Name <span className="text-destructive">*</span></Label>
        <Input
          id="name"
          type="text"
          placeholder="e.g. Friday Night Drafters"
          value={leagueName}
          onChange={(e) => setLeagueName(e.target.value)}
          onBlur={() => setNameTouched(true)}
          minLength={3}
          maxLength={50}
          required
          aria-invalid={nameInvalid || undefined}
        />
      </div>

      <div className="space-y-2">
        <Label>Number of Teams</Label>
        <StepperInput
          value={teamCount}
          min={6}
          max={16}
          onChange={setTeamCount}
          decrementLabel="Decrease team count"
          incrementLabel="Increase team count"
        />
        <p className="text-sm text-muted-foreground">Choose between 6 and 16 teams</p>
      </div>

      <div className="flex items-center gap-3">
        <Checkbox
          id="commissionerOnly"
          checked={!joinAsPlayer}
          onCheckedChange={(v) => setJoinAsPlayer(v !== true)}
        />
        <Label htmlFor="commissionerOnly" className="font-normal cursor-pointer text-muted-foreground">
          Commissioner only — I won&apos;t be managing a team
        </Label>
      </div>

      {error && (
        <p className="text-sm text-destructive">{error}</p>
      )}

      <div className="grid grid-cols-2 gap-3 pt-2">
        <Button type="button" variant="outline" disabled className="w-full">
          ← Back
        </Button>
        <Button type="button" onClick={onNext} className="w-full" disabled={!leagueName.trim()}>
          NEXT →
        </Button>
      </div>

    </div>
  );
}

// ─── Step 2: Sports ───────────────────────────────────────────────────────────

function Step2Sports({
  templates,
  allSportsSeasons,
  teamCount,
  selectedTemplate,
  setSelectedTemplate,
  selectedSports,
  setSelectedSports,
  error,
  onNext,
  onBack,
}: {
  templates: Template[];
  allSportsSeasons: SportSeason[];
  teamCount: number;
  selectedTemplate: string;
  setSelectedTemplate: (v: string) => void;
  selectedSports: Set<string>;
  setSelectedSports: (v: Set<string>) => void;
  error: string | null;
  onNext: () => void;
  onBack: () => void;
}) {
  const toggleSport = (id: string) => {
    const next = new Set(selectedSports);
    if (next.has(id)) next.delete(id);
    else next.add(id);
    setSelectedSports(next);
  };

  const handleTemplateClick = (id: string, sportsSeasonIds: string[]) => {
    setSelectedTemplate(id);
    setSelectedSports(new Set(sportsSeasonIds));
  };

  // When a named template is selected, only show that template's seasons
  // Always exclude seasons with fewer participants than the league's team count
  const activeTemplate = templates.find((t) => t.id === selectedTemplate);
  const eligibleSeasons = allSportsSeasons.filter(
    (s) => s.participantCount >= teamCount || (s.sport.slug === "brackt" && s.fantasySeasonId === null)
  );
  const displaySeasons = selectedTemplate === "customize" || !activeTemplate
    ? eligibleSeasons
    : eligibleSeasons.filter((s) => activeTemplate.sportsSeasonIds.includes(s.id));

  const sortedDisplaySeasons = displaySeasons.toSorted((a, b) => {
    const sportNameDiff = a.sport.name.localeCompare(b.sport.name);
    if (sportNameDiff !== 0) return sportNameDiff;
    return b.year - a.year;
  });

  return (
    <div className="space-y-5">
      <div>
        <h2 className="text-xl font-semibold flex items-center gap-2">
          <span className="w-7 h-7 rounded-full bg-primary text-primary-foreground flex items-center justify-center text-sm font-bold shrink-0">2</span>
          Sports
        </h2>
      </div>

      {templates.length > 0 && (
        <div className="space-y-2">
          {templates.map((t) => (
            <button
              key={t.id}
              type="button"
              onClick={() => handleTemplateClick(t.id, t.sportsSeasonIds)}
              className={cn(
                "w-full text-left rounded-lg border-2 px-4 py-3 flex items-center justify-between transition-all hover:border-primary/60",
                selectedTemplate === t.id ? "border-primary" : "border-border"
              )}
            >
              <div>
                <p className="font-semibold text-sm">{t.name}</p>
                {t.description && (
                  <p className="text-xs text-muted-foreground mt-0.5">{t.description}</p>
                )}
              </div>
              <div className="flex items-center gap-3 shrink-0 ml-4">
                <span className="text-xs text-muted-foreground">
                  {new Set(allSportsSeasons.filter(s => t.sportsSeasonIds.includes(s.id)).map(s => s.sport.name)).size} sports
                </span>
                {selectedTemplate === t.id && (
                  <div className="w-5 h-5 rounded-full bg-primary flex items-center justify-center">
                    <Check className="h-3 w-3 text-primary-foreground" />
                  </div>
                )}
              </div>
            </button>
          ))}
          <button
            type="button"
            onClick={() => setSelectedTemplate("customize")}
            className={cn(
              "w-full text-left rounded-lg border-2 px-4 py-3 flex items-center justify-between transition-all hover:border-primary/60",
              selectedTemplate === "customize" ? "border-primary" : "border-border"
            )}
          >
            <div>
              <p className="font-semibold text-sm">Customize</p>
              <p className="text-xs text-muted-foreground mt-0.5">Pick your own sports seasons</p>
            </div>
            {selectedTemplate === "customize" && (
              <div className="w-5 h-5 rounded-full bg-primary flex items-center justify-center shrink-0 ml-4">
                <Check className="h-3 w-3 text-primary-foreground" />
              </div>
            )}
          </button>
        </div>
      )}

      {/* Named template: read-only alphabetical sport list */}
      {selectedTemplate !== "" && selectedTemplate !== "customize" && (
        <div className="space-y-3">
          <ul className="space-y-1">
            {Object.values(
              displaySeasons.reduce<Record<string, SportSeason["sport"]>>((acc, s) => {
                acc[s.sport.name] = s.sport;
                return acc;
              }, {})
            ).toSorted((a, b) => a.name.localeCompare(b.name)).map((sport) => (
              <li
                key={sport.name}
                className="flex items-center gap-2 text-sm"
                title={isBracktSport(sport) ? BRACKT_SPORT_TOOLTIP : undefined}
              >
                <SportIcon sport={sport} />
                <span>{sport.name}</span>
                {isBracktSport(sport) && <BracktInfoIcon />}
              </li>
            ))}
          </ul>
          <p className="text-xs text-muted-foreground">
            These are the sport seasons included in this template. Switch to Customize to adjust your selection.
          </p>
        </div>
      )}

      {/* Customize: full interactive selector */}
      {selectedTemplate === "customize" && (
        <div className="space-y-3">
          {/* Selected chips */}
          {selectedSports.size > 0 && (
            <div className="space-y-1.5">
              <div className="flex items-center justify-between">
                <p className="text-xs text-muted-foreground">{selectedSports.size} selected</p>
                <button
                  type="button"
                  onClick={() => setSelectedSports(new Set())}
                  className="text-xs text-muted-foreground underline underline-offset-2 hover:text-foreground"
                >
                  Clear all
                </button>
              </div>
              <div className="flex flex-wrap gap-1.5">
                {allSportsSeasons
                  .filter((s) => selectedSports.has(s.id))
                  .toSorted((a, b) => a.sport.name.localeCompare(b.sport.name))
                  .map((s) => (
                    <span
                      key={s.id}
                      className="inline-flex items-center gap-1 pl-2 pr-1 py-0.5 rounded-md bg-primary/10 border border-primary text-primary text-sm font-medium"
                    >
                      <SportIcon sport={s.sport} />
                      {s.name}
                      <button
                        type="button"
                        onClick={() => toggleSport(s.id)}
                        className="ml-0.5 hover:text-primary/60 transition-colors"
                        aria-label={`Remove ${s.sport.name}`}
                      >
                        ×
                      </button>
                    </span>
                  ))}
              </div>
            </div>
          )}

          {/* Full sport picker */}
          <div className="max-h-72 overflow-y-auto pr-1">
            <div className="flex flex-col gap-1">
              {sortedDisplaySeasons.map((s) => {
                const selected = selectedSports.has(s.id);
                return (
                  <button
                    key={s.id}
                    type="button"
                    onClick={() => toggleSport(s.id)}
                    title={isBracktSport(s.sport) ? BRACKT_SPORT_TOOLTIP : undefined}
                    className={cn(
                      "w-full flex items-center justify-between px-3 py-2 rounded-md text-sm border transition-colors",
                      selected
                        ? "bg-primary/10 border-primary text-primary font-medium"
                        : "bg-muted border-border text-foreground hover:border-primary/50"
                    )}
                  >
                    <span className="flex items-center gap-2">
                      <SportIcon sport={s.sport} />
                      {s.name}
                      {isBracktSport(s.sport) && <BracktInfoIcon />}
                    </span>
                    <span className="text-xs font-semibold">{selected ? "Remove" : "Add"}</span>
                  </button>
                );
              })}
              {sortedDisplaySeasons.length === 0 && (
                <p className="text-sm text-muted-foreground">No sports seasons available.</p>
              )}
            </div>
          </div>
        </div>
      )}

      {error && <p className="text-sm text-destructive">{error}</p>}

      <div className="grid grid-cols-2 gap-3 pt-2">
        <Button type="button" variant="outline" onClick={onBack} className="w-full">
          ← Back
        </Button>
        <Button type="button" onClick={onNext} className="w-full">
          NEXT →
        </Button>
      </div>

      {selectedSports.size === 0 && (
        <p className="text-center text-sm text-muted-foreground">
          Select at least one sport season to continue.
        </p>
      )}
    </div>
  );
}

// ─── Step 3: Draft Settings ───────────────────────────────────────────────────

function Step3DraftSettings({
  draftRounds,
  setDraftRounds,
  draftDate,
  setDraftDate,
  draftTime,
  setDraftTime,
  autoStartDraft,
  setAutoStartDraft,
  timerMode,
  setTimerMode,
  draftSpeed,
  setDraftSpeed,
  overnightMode,
  setOvernightMode,
  overnightStart,
  setOvernightStart,
  overnightEnd,
  setOvernightEnd,
  overnightTimezone,
  setOvernightTimezone,
  commishTimezone,
  sportsCount,
  error,
  onNext,
  onBack,
}: {
  draftRounds: number;
  setDraftRounds: (v: number) => void;
  draftDate: string;
  setDraftDate: (v: string) => void;
  draftTime: string;
  setDraftTime: (v: string) => void;
  autoStartDraft: boolean;
  setAutoStartDraft: (v: boolean) => void;
  timerMode: "chess_clock" | "standard";
  setTimerMode: (v: "chess_clock" | "standard") => void;
  draftSpeed: string;
  setDraftSpeed: (v: string) => void;
  overnightMode: "none" | "league" | "per_user";
  setOvernightMode: (v: "none" | "league" | "per_user") => void;
  overnightStart: string;
  setOvernightStart: (v: string) => void;
  overnightEnd: string;
  setOvernightEnd: (v: string) => void;
  overnightTimezone: string;
  setOvernightTimezone: (v: string) => void;
  commishTimezone: string | null;
  sportsCount: number;
  error: string | null;
  onNext: () => void;
  onBack: () => void;
}) {
  const minRounds = sportsCount;
  const flexSpots = Math.max(0, draftRounds - sportsCount);
  const { draftInitialTime, draftIncrementTime } = parseDraftSpeed(draftSpeed, timerMode);
  const showOvernightPause = draftInitialTime >= 3600 || draftIncrementTime >= 600;

  const localTz = useLocalTimezone();

  useEffect(() => {
    if (!showOvernightPause) setOvernightMode("none");
  }, [showOvernightPause]); // eslint-disable-line react-hooks/exhaustive-deps


  return (
    <div className="space-y-8">
      <div>
        <h2 className="text-xl font-semibold flex items-center gap-2">
          <span className="w-7 h-7 rounded-full bg-primary text-primary-foreground flex items-center justify-center text-sm font-bold shrink-0">3</span>
          Draft Settings
        </h2>
      </div>

      {/* Draft Rounds */}
      <div className="space-y-2">
        <Label>Draft Rounds</Label>
        <StepperInput
          value={draftRounds}
          min={Math.max(1, minRounds)}
          max={50}
          onChange={setDraftRounds}
          decrementLabel="Decrease rounds"
          incrementLabel="Increase rounds"
        />
        <p className="text-sm text-muted-foreground">
          Minimum: {minRounds} (based on sports selected)
          {sportsCount > 0 && ` · Flex spots: ${flexSpots}`}
        </p>
      </div>

      {/* Draft Date & Time */}
      <div className="space-y-2">
        <Label>Draft Date &amp; Time</Label>
        <div className="grid grid-cols-2 gap-2">
          <Input
            type="date"
            value={draftDate}
            onChange={(e) => setDraftDate(e.target.value)}
            min={new Date().toISOString().slice(0, 10)}
          />
          <Input
            type="time"
            value={draftTime}
            onChange={(e) => setDraftTime(e.target.value)}
          />
        </div>
        <p className="text-sm text-muted-foreground">
          This can be changed before the draft starts.
          {localTz && <> Times are in your local timezone ({localTz}).</>}
        </p>
        {draftDate && draftTime && (
          <div className="flex items-center gap-3 pt-1">
            <Checkbox
              id="autoStartDraft"
              checked={autoStartDraft}
              onCheckedChange={(v) => setAutoStartDraft(v === true)}
            />
            <Label htmlFor="autoStartDraft" className="font-normal cursor-pointer">
              Auto-start at scheduled time
            </Label>
          </div>
        )}
        <input type="hidden" name="autoStartDraft" value={draftDate && draftTime && autoStartDraft ? "on" : ""} />
      </div>

      {/* Timer Mode */}
      <div className="space-y-2">
        <Label>Timer Mode</Label>
        <TimerModeSelector value={timerMode} onChange={setTimerMode} />
      </div>

      {/* Draft Speed */}
      <div className="space-y-2">
        <Label>Draft Speed</Label>
        <DraftSpeedPicker timerMode={timerMode} value={draftSpeed} onChange={setDraftSpeed} />
      </div>

      {/* Overnight Pause */}
      <OvernightPauseSettings
        show={showOvernightPause}
        mode={overnightMode}
        onModeChange={setOvernightMode}
        start={overnightStart}
        onStartChange={setOvernightStart}
        end={overnightEnd}
        onEndChange={setOvernightEnd}
        timezone={overnightTimezone}
        onTimezoneChange={setOvernightTimezone}
        commishTimezone={commishTimezone}
      />

      {error && <p className="text-sm text-destructive">{error}</p>}

      <div className="grid grid-cols-2 gap-3 pt-2">
        <Button type="button" variant="outline" onClick={onBack} className="w-full">
          ← Back
        </Button>
        <Button type="button" onClick={onNext} className="w-full" disabled={!draftDate || !draftTime}>
          NEXT →
        </Button>
      </div>
    </div>
  );
}

// ─── Step 4: Scoring ──────────────────────────────────────────────────────────

function Step4Scoring({
  scoringPreset,
  setScoringPreset,
  scoringRules,
  setScoringRules,
  onNext,
  onBack,
}: {
  scoringPreset: "brackt" | "omnifantasy" | "custom";
  setScoringPreset: (v: "brackt" | "omnifantasy" | "custom") => void;
  scoringRules: ScoringRules;
  setScoringRules: (v: ScoringRules) => void;
  onNext: () => void;
  onBack: () => void;
}) {
  return (
    <div className="space-y-5">
      <div>
        <h2 className="text-xl font-semibold flex items-center gap-2">
          <span className="w-7 h-7 rounded-full bg-primary text-primary-foreground flex items-center justify-center text-sm font-bold shrink-0">4</span>
          Scoring
        </h2>
        <p className="text-sm text-muted-foreground mt-1">
          Points awarded to teams based on how their drafted participants finish in each sport&apos;s season.
        </p>
      </div>

      <ScoringPresetPicker
        preset={scoringPreset}
        onPresetChange={setScoringPreset}
        rules={scoringRules}
        onRulesChange={setScoringRules}
      />

      <div className="grid grid-cols-2 gap-3 pt-2">
        <Button type="button" variant="outline" onClick={onBack} className="w-full">
          ← Back
        </Button>
        <Button type="button" onClick={onNext} className="w-full">
          NEXT →
        </Button>
      </div>
    </div>
  );
}

// ─── Step 5: Review ───────────────────────────────────────────────────────────

function StepReview({
  leagueName,
  teamCount,
  joinAsPlayer,
  selectedSports,
  allSportsSeasons,
  draftRounds,
  draftDate,
  draftTime,
  timerMode,
  draftSpeed,
  overnightMode,
  overnightStart,
  overnightEnd,
  overnightTimezone,
  scoringPreset,
  scoringRules,
  isAuthenticated,
  authTab,
  setAuthTab,
  displayName,
  setDisplayName,
  authEmail,
  setAuthEmail,
  authPassword,
  setAuthPassword,
  userTimezone,
  setUserTimezone,
  onSocialLogin,
  error,
  submitting,
  onSubmit,
  onBack,
  onEditStep,
}: {
  leagueName: string;
  teamCount: number;
  joinAsPlayer: boolean;
  selectedSports: Set<string>;
  allSportsSeasons: SportSeason[];
  draftRounds: number;
  draftDate: string;
  draftTime: string;
  timerMode: "chess_clock" | "standard";
  draftSpeed: string;
  overnightMode: "none" | "league" | "per_user";
  overnightStart: string;
  overnightEnd: string;
  overnightTimezone: string;
  scoringPreset: "brackt" | "omnifantasy" | "custom";
  scoringRules: ScoringRules;
  isAuthenticated: boolean;
  authTab: "create" | "login";
  setAuthTab: (v: "create" | "login") => void;
  displayName: string;
  setDisplayName: (v: string) => void;
  authEmail: string;
  setAuthEmail: (v: string) => void;
  authPassword: string;
  setAuthPassword: (v: string) => void;
  userTimezone: string;
  setUserTimezone: (v: string) => void;
  onSocialLogin: (provider: "google" | "discord") => void;
  error: string | null;
  submitting: boolean;
  onSubmit: () => void;
  onBack: () => void;
  onEditStep: (n: number) => void;
}) {
  const selectedSeasons = allSportsSeasons.filter((s) => selectedSports.has(s.id));

  const draftDateTimeStr =
    draftDate && draftTime
      ? `${format(new Date(draftDate + "T00:00:00"), "MMMM do, yyyy")} at ${formatTime12h(draftTime)}`
      : "Not set (required before starting draft)";

  const tzLabel = TIMEZONE_OPTIONS.flatMap((g) => g.zones).find((z) => z.value === overnightTimezone)?.label ?? overnightTimezone;
  const overnightModeLine = overnightMode === "none" ? "None" : overnightMode === "league" ? "League-wide" : "Per user";
  const overnightTimeLine = overnightMode !== "none" ? `${formatTime12h(overnightStart)} – ${formatTime12h(overnightEnd)} (${tzLabel})` : null;

  const maxPoints = Math.max(...PLACEMENT_LABELS.map(({ key }) => scoringRules[key]));
  const uniqueSportsCount = new Set(selectedSeasons.map((s) => s.sport.id)).size;

  return (
    <div className="space-y-4">
      <div>
        <h2 className="text-xl font-semibold flex items-center gap-2">
          <span className="w-7 h-7 rounded-full bg-primary text-primary-foreground flex items-center justify-center text-sm font-bold shrink-0">5</span>
          Review
        </h2>
        <p className="text-sm text-muted-foreground mt-1">
          Everything look good? You can edit any section before creating.
        </p>
      </div>

      <ReviewSection icon={Swords} title="League Basics" onEdit={() => onEditStep(1)}>
        <div className="flex items-center justify-between gap-3">
          <p className="text-lg font-bold text-foreground truncate">{leagueName}</p>
          <div className="flex items-center gap-2 shrink-0">
            <Badge variant="secondary">{teamCount} teams</Badge>
            {!joinAsPlayer && <Badge variant="default">Commish only</Badge>}
          </div>
        </div>
      </ReviewSection>

      <ReviewSection icon={Trophy} title="Sports" onEdit={() => onEditStep(2)}>
        <p className="text-sm text-muted-foreground">
          <span className="text-primary font-semibold">{uniqueSportsCount} sport{uniqueSportsCount !== 1 ? "s" : ""}</span>
        </p>
        <ul className="mt-1 space-y-1">
          {selectedSeasons.toSorted((a, b) => a.name.localeCompare(b.name)).map((s) => (
            <li key={s.id} className="flex items-center gap-2 text-sm text-foreground">
              <SportIcon sport={s.sport} />
              {s.name}
            </li>
          ))}
        </ul>
      </ReviewSection>

      <ReviewSection icon={CalendarClock} title="Draft Settings" onEdit={() => onEditStep(3)}>
        <dl className="grid grid-cols-[auto_1fr] gap-x-4 gap-y-1 text-sm">
          <dt className="text-muted-foreground">Rounds</dt>
          <dd className="text-foreground">{draftRounds}</dd>
          <dt className="text-muted-foreground">Date &amp; Time</dt>
          <dd className="text-primary font-medium">{draftDateTimeStr}</dd>
          <dt className="text-muted-foreground">Timer</dt>
          <dd className="text-foreground">{timerMode === "chess_clock" ? "Chess Clock" : "Standard"}</dd>
          <dt className="text-muted-foreground">Speed</dt>
          <dd className="text-foreground">{formatDraftSpeed(draftSpeed, timerMode)}</dd>
          <dt className="text-muted-foreground">Pause</dt>
          <dd className="text-foreground">
            {overnightModeLine}
            {overnightTimeLine && <div className="text-muted-foreground text-xs mt-0.5">{overnightTimeLine}</div>}
          </dd>
        </dl>
      </ReviewSection>

      <ReviewSection icon={Star} title="Scoring" onEdit={() => onEditStep(4)}>
        <p className="text-xs text-muted-foreground mb-2">
          {scoringPreset === "brackt" ? "Brackt Default" : scoringPreset === "omnifantasy" ? "Omnifantasy Default" : "Custom"}
        </p>
        <div className="grid grid-cols-2 gap-x-4 gap-y-2">
          {PLACEMENT_LABELS.map(({ key, label, color }) => {
            const val = scoringRules[key];
            const pct = maxPoints > 0 ? (val / maxPoints) * 100 : 0;
            return (
              <div key={key} className="flex items-center gap-2">
                <span className={cn("text-xs font-semibold w-6 shrink-0", color)}>{label}</span>
                <div className="flex-1 h-1.5 bg-muted rounded-full overflow-hidden">
                  <div className="h-full bg-primary rounded-full" style={{ width: `${pct}%` }} />
                </div>
                <span className="text-xs font-semibold w-6 text-right shrink-0">{val}</span>
              </div>
            );
          })}
        </div>
      </ReviewSection>

      {!isAuthenticated && (
        <WizardAuthForm
          authTab={authTab}
          setAuthTab={setAuthTab}
          displayName={displayName}
          setDisplayName={setDisplayName}
          authEmail={authEmail}
          setAuthEmail={setAuthEmail}
          authPassword={authPassword}
          setAuthPassword={setAuthPassword}
          userTimezone={userTimezone}
          setUserTimezone={setUserTimezone}
          onSocialLogin={onSocialLogin}
          submitting={submitting}
        />
      )}

      {error && <p className="text-sm text-destructive">{error}</p>}

      <div className="grid grid-cols-2 gap-3 pt-2">
        <Button type="button" variant="outline" onClick={onBack} disabled={submitting} className="w-full">
          ← Back
        </Button>
        <Button type="button" onClick={onSubmit} disabled={submitting} className="w-full">
          {submitting
            ? "Creating…"
            : isAuthenticated
            ? "CREATE LEAGUE"
            : authTab === "create"
            ? "CREATE ACCOUNT & LEAGUE"
            : "LOG IN & CREATE LEAGUE"}
        </Button>
      </div>
    </div>
  );
}

// ─── Main Component ───────────────────────────────────────────────────────────

export default function NewLeague({ loaderData }: Route.ComponentProps) {
  const { templates, allSportsSeasons, commishTimezone, isAuthenticated } = loaderData;
  const fetcher = useFetcher();

  // Wizard
  const [step, setStep] = useState(1);

  // Step-level validation error
  const [stepError, setStepError] = useState<string | null>(null);

  // Step 1
  const [leagueName, setLeagueName] = useState("");
  const [teamCount, setTeamCount] = useState(12);
  const [joinAsPlayer, setJoinAsPlayer] = useState(true);

  // Step 2
  const [selectedTemplate, setSelectedTemplate] = useState("");
  const [selectedSports, setSelectedSports] = useState<Set<string>>(new Set());

  // Step 3
  const [draftRounds, setDraftRounds] = useState(20);
  const [draftDate, setDraftDate] = useState("");
  const [draftTime, setDraftTime] = useState("");
  const [autoStartDraft, setAutoStartDraft] = useState(false);
  const [timerMode, setTimerMode] = useState<"chess_clock" | "standard">("chess_clock");
  const [draftSpeed, setDraftSpeed] = useState("standard");
  const [overnightMode, setOvernightMode] = useState<"none" | "league" | "per_user">("none");
  const [overnightStart, setOvernightStart] = useState("23:00");
  const [overnightEnd, setOvernightEnd] = useState("07:00");
  const [overnightTimezone, setOvernightTimezone] = useState(commishTimezone ?? "");

  // Step 4
  const [scoringPreset, setScoringPreset] = useState<"brackt" | "omnifantasy" | "custom">("brackt");
  const [scoringRules, setScoringRules] = useState<ScoringRules>({ ...DEFAULT_SCORING_RULES });

  // Step 5
  const [authTab, setAuthTab] = useState<"create" | "login">("create");
  const [displayName, setDisplayName] = useState("");
  const [authEmail, setAuthEmail] = useState("");
  const [authPassword, setAuthPassword] = useState("");
  const [authError, setAuthError] = useState<string | null>(null);
  const [authLoading, setAuthLoading] = useState(false);
  const [userTimezone, setUserTimezone] = useState("");

  // Detect browser timezone on mount
  useEffect(() => {
    const detected = Intl.DateTimeFormat().resolvedOptions().timeZone;
    if (!overnightTimezone) setOvernightTimezone(detected);
    setUserTimezone(detected);
  }, []); // eslint-disable-line react-hooks/exhaustive-deps

  // Recalculate default rounds whenever sports count changes
  const sportsCount = selectedSports.size;
  useEffect(() => {
    setDraftRounds(defaultRounds(sportsCount));
  }, [sportsCount]); // eslint-disable-line react-hooks/exhaustive-deps

  const isSubmitting = fetcher.state !== "idle" || authLoading;
  const submitError =
    fetcher.data && typeof fetcher.data === "object" && "error" in fetcher.data
      ? String((fetcher.data as { error: unknown }).error)
      : null;

  function buildFormData(): FormData {
    return buildFormDataFromSnapshot({
      leagueName, teamCount, joinAsPlayer, selectedTemplate,
      selectedSports: [...selectedSports],
      draftRounds, draftDate, draftTime, autoStartDraft, timerMode, draftSpeed,
      overnightMode, overnightStart, overnightEnd, overnightTimezone,
      scoringPreset, scoringRules, userTimezone,
    });
  }

  async function handleFinalSubmit() {
    if (isAuthenticated) {
      fetcher.submit(buildFormData(), { method: "post" });
      return;
    }

    setAuthError(null);
    setAuthLoading(true);

    if (authTab === "create") {
      const { error } = await authClient.signUp.email({
        name: displayName,
        email: authEmail,
        password: authPassword,
      });
      if (error) {
        setAuthError(error.message ?? "Registration failed. Please try again.");
        setAuthLoading(false);
        return;
      }
    } else {
      const { error } = await authClient.signIn.email({
        email: authEmail,
        password: authPassword,
      });
      if (error) {
        setAuthError(error.message ?? "Sign in failed. Please check your credentials.");
        setAuthLoading(false);
        return;
      }
    }

    fetcher.submit(buildFormData(), { method: "post" });
    setAuthLoading(false);
  }

  async function handleSocialLogin(provider: "google" | "discord") {
    saveWizardState({
      leagueName, teamCount, joinAsPlayer, selectedTemplate,
      selectedSports: [...selectedSports],
      draftRounds, draftDate, draftTime, autoStartDraft, timerMode, draftSpeed,
      overnightMode, overnightStart, overnightEnd, overnightTimezone,
      scoringPreset, scoringRules, userTimezone,
    });
    await authClient.signIn.social({ provider, callbackURL: "/leagues/creating" });
  }

  function goNext() {
    setStepError(null);
    if (step === 1 && leagueName.trim().length < 3) {
      setStepError("League name must be at least 3 characters.");
      return;
    }
    if (step === 2 && selectedSports.size === 0) {
      setStepError("Please select at least one sport season.");
      return;
    }
    if (step === 3 && (!draftDate || !draftTime)) {
      setStepError("Please set a draft date and time.");
      return;
    }
    if (step === 3 && draftRounds < sportsCount) {
      setStepError(`Draft rounds must be at least ${sportsCount} (number of sports selected).`);
      return;
    }
    setStep((s) => s + 1);
  }

  function goBack() {
    setStepError(null);
    setStep((s) => s - 1);
  }

  function goToStep(n: number) {
    if (n < step) {
      setStepError(null);
      setStep(n);
    }
  }

  const STEPS = ["Basics", "Sports", "Draft Settings", "Scoring", "Review"];

  return (
    <div className="min-h-screen bg-background">
      <div className="max-w-lg mx-auto px-4 py-4">
        <div className="mb-6">
          <h1 className="text-3xl font-bold">Start a New League</h1>
          <p className="text-muted-foreground mt-1">
            Create your fantasy league and invite your friends to compete.
          </p>
        </div>

        <WizardStepper currentStep={step} steps={STEPS} onStepClick={goToStep} />

        <Card className="py-0">
          <CardContent className="p-5">
            {step === 1 && (
              <Step1LeagueBasics
                leagueName={leagueName}
                setLeagueName={setLeagueName}
                teamCount={teamCount}
                setTeamCount={setTeamCount}
                joinAsPlayer={joinAsPlayer}
                setJoinAsPlayer={setJoinAsPlayer}
                error={stepError}
                onNext={goNext}
              />
            )}
            {step === 2 && (
              <Step2Sports
                templates={templates}
                allSportsSeasons={allSportsSeasons}
                teamCount={teamCount}
                selectedTemplate={selectedTemplate}
                setSelectedTemplate={setSelectedTemplate}
                selectedSports={selectedSports}
                setSelectedSports={setSelectedSports}
                error={stepError}
                onNext={goNext}
                onBack={goBack}
              />
            )}
            {step === 3 && (
              <Step3DraftSettings
                draftRounds={draftRounds}
                setDraftRounds={setDraftRounds}
                draftDate={draftDate}
                setDraftDate={setDraftDate}
                draftTime={draftTime}
                setDraftTime={setDraftTime}
                autoStartDraft={autoStartDraft}
                setAutoStartDraft={setAutoStartDraft}
                timerMode={timerMode}
                setTimerMode={(mode) => {
                  setTimerMode(mode);
                  setDraftSpeed(mode === "chess_clock" ? "standard" : "90");
                }}
                draftSpeed={draftSpeed}
                setDraftSpeed={(newSpeed) => {
                  if (isSlowPreset(newSpeed) && !isSlowPreset(draftSpeed)) {
                    setOvernightMode("league");
                    setOvernightStart("23:00");
                    setOvernightEnd("07:00");
                  }
                  setDraftSpeed(newSpeed);
                }}
                overnightMode={overnightMode}
                setOvernightMode={setOvernightMode}
                overnightStart={overnightStart}
                setOvernightStart={setOvernightStart}
                overnightEnd={overnightEnd}
                setOvernightEnd={setOvernightEnd}
                overnightTimezone={overnightTimezone}
                setOvernightTimezone={setOvernightTimezone}
                commishTimezone={commishTimezone}
                sportsCount={sportsCount}
                error={stepError}
                onNext={goNext}
                onBack={goBack}
              />
            )}
            {step === 4 && (
              <Step4Scoring
                scoringPreset={scoringPreset}
                setScoringPreset={setScoringPreset}
                scoringRules={scoringRules}
                setScoringRules={setScoringRules}
                onNext={goNext}
                onBack={goBack}
              />
            )}
            {step === 5 && (
              <StepReview
                leagueName={leagueName}
                teamCount={teamCount}
                joinAsPlayer={joinAsPlayer}
                selectedSports={selectedSports}
                allSportsSeasons={allSportsSeasons}
                draftRounds={draftRounds}
                draftDate={draftDate}
                draftTime={draftTime}
                timerMode={timerMode}
                draftSpeed={draftSpeed}
                overnightMode={overnightMode}
                overnightStart={overnightStart}
                overnightEnd={overnightEnd}
                overnightTimezone={overnightTimezone}
                scoringPreset={scoringPreset}
                scoringRules={scoringRules}
                isAuthenticated={isAuthenticated}
                authTab={authTab}
                setAuthTab={setAuthTab}
                displayName={displayName}
                setDisplayName={setDisplayName}
                authEmail={authEmail}
                setAuthEmail={setAuthEmail}
                authPassword={authPassword}
                setAuthPassword={setAuthPassword}
                userTimezone={userTimezone}
                setUserTimezone={setUserTimezone}
                onSocialLogin={handleSocialLogin}
                error={submitError ?? authError}
                submitting={isSubmitting}
                onSubmit={handleFinalSubmit}
                onBack={goBack}
                onEditStep={goToStep}
              />
            )}
          </CardContent>
        </Card>

      </div>
    </div>
  );
}
