import { useState, useEffect, useCallback } from "react";
import { Form, useBeforeUnload, useBlocker, useLocation, useNavigation, type BlockerFunction } from "react-router";
import { format } from "date-fns";
import {
  AlertTriangle,
  Bell,
  ListOrdered,
  Shield,
  Swords,
  TrendingUp,
  Trophy,
  Users,
} from "lucide-react";
import { type ScoringRules, DEFAULT_SCORING_RULES } from "~/lib/scoring-types";
import { getInitialDraftSpeed } from "~/lib/draft-timer";
import { buildDraftOrderTeams } from "~/lib/draft-order";
import type { Route } from "./+types/$leagueId.settings";
export { action, loader } from "./$leagueId.settings.server";
import { OMNIFANTASY_SCORING } from "~/components/league/ScoringPresetPicker";
import { Button } from "~/components/ui/button";
import { cn } from "~/lib/utils";
import { SettingsMessage } from "~/components/league/settings/SettingsMessages";
import { LeagueSettingsHeader } from "~/components/league/settings/LeagueSettingsHeader";
import {
  SettingsDesktopNav,
  SettingsMobileGridNav,
  SettingsMobileSectionPill,
  type SettingsGridSection,
} from "~/components/league/settings/SettingsNavigation";
import { SettingsSaveBar } from "~/components/league/settings/SettingsSaveBar";
import { LeaveSettingsDialog, SwitchSettingsSectionDialog } from "~/components/league/settings/UnsavedChangesDialogs";
import { DraftOrderSection } from "~/components/league/settings/DraftOrderSection";
import { LeagueBasicsSection } from "~/components/league/settings/LeagueBasicsSection";
import { DraftSetupSection } from "~/components/league/settings/DraftSetupSection";
import { SportsSection } from "~/components/league/settings/SportsSection";
import { ScoringSection } from "~/components/league/settings/ScoringSection";
import { NotificationsSection } from "~/components/league/settings/NotificationsSection";
import { PeopleSection } from "~/components/league/settings/PeopleSection";
import { HistoryDangerSection } from "~/components/league/settings/HistoryDangerSection";
import { toast } from "sonner";

export function meta({ data }: Route.MetaArgs): Route.MetaDescriptors {
  return [{ title: `Settings — ${data?.league?.name ?? "League"} - Brackt` }];
}

const BRACKT_SLUG = "brackt";

type LoaderSeason = Route.ComponentProps["loaderData"]["season"];
type LoaderSportsSeason = Route.ComponentProps["loaderData"]["allSportsSeasons"][number] & {
  fantasySeasonId?: string | null;
};

function getInitialScoringPreset(season: LoaderSeason) {
  const rules: ScoringRules = {
    pointsFor1st: season?.pointsFor1st ?? DEFAULT_SCORING_RULES.pointsFor1st,
    pointsFor2nd: season?.pointsFor2nd ?? DEFAULT_SCORING_RULES.pointsFor2nd,
    pointsFor3rd: season?.pointsFor3rd ?? DEFAULT_SCORING_RULES.pointsFor3rd,
    pointsFor4th: season?.pointsFor4th ?? DEFAULT_SCORING_RULES.pointsFor4th,
    pointsFor5th: season?.pointsFor5th ?? DEFAULT_SCORING_RULES.pointsFor5th,
    pointsFor6th: season?.pointsFor6th ?? DEFAULT_SCORING_RULES.pointsFor6th,
    pointsFor7th: season?.pointsFor7th ?? DEFAULT_SCORING_RULES.pointsFor7th,
    pointsFor8th: season?.pointsFor8th ?? DEFAULT_SCORING_RULES.pointsFor8th,
  };
  if (Object.entries(DEFAULT_SCORING_RULES).every(([k, v]) => rules[k as keyof ScoringRules] === v)) return "brackt" as const;
  if (Object.entries(OMNIFANTASY_SCORING).every(([k, v]) => rules[k as keyof ScoringRules] === v)) return "omnifantasy" as const;
  return "custom" as const;
}

function hasFantasySeasonId(season: object): boolean {
  return "fantasySeasonId" in season && typeof season.fantasySeasonId === "string" && season.fantasySeasonId.length > 0;
}

function findTemplateBrackt(seasons: LoaderSportsSeason[]): LoaderSportsSeason | undefined {
  return seasons.find((ss) => ss.sport.slug === BRACKT_SLUG && !hasFantasySeasonId(ss));
}

function getInitialSelectedSports({
  season,
  allSportsSeasons,
}: {
  season: LoaderSeason;
  allSportsSeasons: LoaderSportsSeason[];
}) {
  const linkedSeasons = season?.seasonSports?.map((s) => s.sportsSeason) ?? [];
  const linkedIds = linkedSeasons.map((ss) => ss.id);
  const templateBrackt = findTemplateBrackt(allSportsSeasons);
  const privateBrackt = linkedSeasons.find((ss) => ss.sport.slug === BRACKT_SLUG && hasFantasySeasonId(ss));

  if (templateBrackt && privateBrackt) {
    return new Set(linkedIds.map((id) => (id === privateBrackt.id ? templateBrackt.id : id)));
  }
  return new Set(linkedIds);
}

const SETTINGS_SECTIONS = [
  { id: "league-basics", label: "League Basics" },
  { id: "draft-setup", label: "Draft Settings" },
  { id: "draft-order", label: "Draft Order" },
  { id: "sports", label: "Sports" },
  { id: "scoring", label: "Scoring" },
  { id: "notifications", label: "Notifications" },
  { id: "people", label: "People" },
  { id: "history-danger", label: "History & Danger" },
] as const;

type SettingsSectionId = (typeof SETTINGS_SECTIONS)[number]["id"];

const UPDATE_SECTIONS: SettingsSectionId[] = [
  "league-basics",
  "draft-setup",
  "sports",
  "scoring",
  "notifications",
];

export default function LeagueSettings({ loaderData, actionData }: Route.ComponentProps) {
  const {
    league, season, teams, teamCount, teamsWithOwners, allSportsSeasons,
    draftSlots, isAdmin, leagueMembers, ownerMap, commissioners, currentUserId, commishTimezone,
  } = loaderData;

  const navigation = useNavigation();
  const location = useLocation();
  const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
  const [mobileView, setMobileView] = useState<"grid" | "section">("grid");
  const [activeSection, setActiveSection] = useState<SettingsSectionId>("league-basics");
  const [pendingSection, setPendingSection] = useState<SettingsSectionId | null>(null);

  // Two separate dirty flags: one for the multi-section settings form, one for draft order
  const [hasUnsavedSettingsChanges, setHasUnsavedSettingsChanges] = useState(false);
  const [hasDraftOrderChanges, setHasDraftOrderChanges] = useState(false);

  // Settings form state
  const [leagueName, setLeagueName] = useState(league.name);
  const [isPublicDraftBoard, setIsPublicDraftBoard] = useState(league.isPublicDraftBoard);
  const [discordWebhookUrl, setDiscordWebhookUrl] = useState(league.discordWebhookUrl ?? "");
  const [discordPicksAnnouncementEnabled, setDiscordPicksAnnouncementEnabled] = useState(league.discordPicksAnnouncementEnabled);
  const [teamCountValue, setTeamCountValue] = useState<number>(teamCount);
  const [timerMode, setTimerMode] = useState<"chess_clock" | "standard">(season?.draftTimerMode ?? "chess_clock");
  const [overnightMode, setOvernightMode] = useState<"none" | "league" | "per_user">(season?.overnightPauseMode ?? "none");
  const [overnightStart, setOvernightStart] = useState(season?.overnightPauseStart ?? "23:00");
  const [overnightEnd, setOvernightEnd] = useState(season?.overnightPauseEnd ?? "07:00");
  const [overnightTimezone, setOvernightTimezone] = useState(season?.overnightPauseTimezone ?? commishTimezone ?? "");
  const [scoringPreset, setScoringPreset] = useState<"brackt" | "omnifantasy" | "custom">(() => getInitialScoringPreset(season));
  const [scoringRules, setScoringRules] = useState<ScoringRules>({
    pointsFor1st: season?.pointsFor1st ?? DEFAULT_SCORING_RULES.pointsFor1st,
    pointsFor2nd: season?.pointsFor2nd ?? DEFAULT_SCORING_RULES.pointsFor2nd,
    pointsFor3rd: season?.pointsFor3rd ?? DEFAULT_SCORING_RULES.pointsFor3rd,
    pointsFor4th: season?.pointsFor4th ?? DEFAULT_SCORING_RULES.pointsFor4th,
    pointsFor5th: season?.pointsFor5th ?? DEFAULT_SCORING_RULES.pointsFor5th,
    pointsFor6th: season?.pointsFor6th ?? DEFAULT_SCORING_RULES.pointsFor6th,
    pointsFor7th: season?.pointsFor7th ?? DEFAULT_SCORING_RULES.pointsFor7th,
    pointsFor8th: season?.pointsFor8th ?? DEFAULT_SCORING_RULES.pointsFor8th,
  });
  const [draftSpeed, setDraftSpeed] = useState(() => getInitialDraftSpeed(season));
  const [selectedSports, setSelectedSports] = useState<Set<string>>(
    () => getInitialSelectedSports({ season, allSportsSeasons })
  );
  const [draftRounds, setDraftRounds] = useState<number>(season?.draftRounds || 20);
  const [draftDate, setDraftDate] = useState<Date | undefined>(
    season?.draftDateTime ? new Date(season.draftDateTime) : undefined
  );
  const [draftTime, setDraftTime] = useState<string>(
    season?.draftDateTime ? format(new Date(season.draftDateTime), "HH:mm") : ""
  );
  const [autoStartDraft, setAutoStartDraft] = useState<boolean>(season?.autoStartDraft ?? false);

  // Draft order state (separate form, separate dirty flag)
  const [draftOrderTeams, setDraftOrderTeams] = useState<string[]>(
    () => buildDraftOrderTeams(draftSlots, teams)
  );

  const [copied, setCopied] = useState(false);

  // Sync draft order from loader after randomization or other server updates
  useEffect(() => {
    setDraftOrderTeams(buildDraftOrderTeams(draftSlots, teams));
    setHasDraftOrderChanges(false);
  }, [draftSlots, teams]);

  // Re-mark dirty if the settings save returned an error. Guard against non-settings errors
  // (e.g. draft-order) which carry a `section` field.
  useEffect(() => {
    if (navigation.state !== "idle") return;
    if (actionData && "error" in actionData && !("section" in actionData)) {
      setHasUnsavedSettingsChanges(true);
    }
  }, [actionData, navigation.state]);

  useEffect(() => {
    if (navigation.state !== "idle") return;
    if (actionData && "intent" in actionData && actionData.intent === "update" && actionData.success) {
      toast.success(actionData.message ?? "Settings saved!");
      setHasUnsavedSettingsChanges(false);
    }
  }, [actionData, navigation.state]);

  const canEditSports = season?.status === "pre_draft";
  const canEditTeamCount = season?.status === "pre_draft";
  const canEditDraftOrder = season?.status === "pre_draft";
  const canEditDraftRounds = season?.status === "pre_draft";
  const minTeamCount = Math.max(6, teamsWithOwners);
  const sportsCount = selectedSports.size;
  const minRounds = sportsCount;
  const recommendedRounds = Math.ceil(sportsCount * 1.25);
  const flexSpots = Math.max(0, draftRounds - sportsCount);
  const updateIntent = navigation.formData?.get("intent");
  const draftOrderSet = draftSlots.length === teams.length && teams.length > 0;
  const showUpdateForm = UPDATE_SECTIONS.includes(activeSection);
  const isSubmittingSettings = navigation.state === "submitting" && updateIntent === "update";
  const isSubmittingDraftOrder = navigation.state === "submitting" && updateIntent === "set-draft-order";
  const shouldWarnUnsaved = (hasUnsavedSettingsChanges || hasDraftOrderChanges) && !isSubmittingSettings && !isSubmittingDraftOrder;
  const blocker = useBlocker(
    useCallback<BlockerFunction>(
      ({ nextLocation }) => shouldWarnUnsaved && nextLocation.pathname !== location.pathname,
      [shouldWarnUnsaved, location.pathname]
    )
  );
  const inviteUrl = season?.inviteCode
    ? `${typeof window !== "undefined" ? window.location.origin : ""}/i/${season.inviteCode}`
    : "";

  const displaySportsSeasons = (() => {
    const templateBrackt = findTemplateBrackt(allSportsSeasons);
    if (!templateBrackt) return allSportsSeasons;
    return allSportsSeasons.filter((ss) => !(ss.sport.slug === BRACKT_SLUG && hasFantasySeasonId(ss)));
  })();

  useBeforeUnload(
    (event) => {
      if (!shouldWarnUnsaved) return;
      event.preventDefault();
      event.returnValue = "";
    },
    { capture: true }
  );

  const markDirty = () => setHasUnsavedSettingsChanges(true);

  const resetSettingsFormState = () => {
    setLeagueName(league.name);
    setIsPublicDraftBoard(league.isPublicDraftBoard);
    setDiscordWebhookUrl(league.discordWebhookUrl ?? "");
    setDiscordPicksAnnouncementEnabled(league.discordPicksAnnouncementEnabled);
    setTeamCountValue(teamCount);
    setTimerMode(season?.draftTimerMode ?? "chess_clock");
    setDraftSpeed(getInitialDraftSpeed(season));
    setOvernightMode(season?.overnightPauseMode ?? "none");
    setOvernightStart(season?.overnightPauseStart ?? "23:00");
    setOvernightEnd(season?.overnightPauseEnd ?? "07:00");
    setOvernightTimezone(season?.overnightPauseTimezone ?? commishTimezone ?? "");
    setScoringPreset(getInitialScoringPreset(season));
    setScoringRules({
      pointsFor1st: season?.pointsFor1st ?? DEFAULT_SCORING_RULES.pointsFor1st,
      pointsFor2nd: season?.pointsFor2nd ?? DEFAULT_SCORING_RULES.pointsFor2nd,
      pointsFor3rd: season?.pointsFor3rd ?? DEFAULT_SCORING_RULES.pointsFor3rd,
      pointsFor4th: season?.pointsFor4th ?? DEFAULT_SCORING_RULES.pointsFor4th,
      pointsFor5th: season?.pointsFor5th ?? DEFAULT_SCORING_RULES.pointsFor5th,
      pointsFor6th: season?.pointsFor6th ?? DEFAULT_SCORING_RULES.pointsFor6th,
      pointsFor7th: season?.pointsFor7th ?? DEFAULT_SCORING_RULES.pointsFor7th,
      pointsFor8th: season?.pointsFor8th ?? DEFAULT_SCORING_RULES.pointsFor8th,
    });
    setSelectedSports(getInitialSelectedSports({ season, allSportsSeasons }));
    setDraftRounds(season?.draftRounds || 20);
    setDraftDate(season?.draftDateTime ? new Date(season.draftDateTime) : undefined);
    setDraftTime(season?.draftDateTime ? format(new Date(season.draftDateTime), "HH:mm") : "");
    setAutoStartDraft(season?.autoStartDraft ?? false);
    setHasUnsavedSettingsChanges(false);
  };

  const resetDraftOrder = () => {
    setDraftOrderTeams(
      draftSlots.length > 0 ? draftSlots.map((s) => s.teamId) : teams.map((t) => t.id)
    );
    setHasDraftOrderChanges(false);
  };

  const handleSectionChange = (sectionId: SettingsSectionId, { mobile = false } = {}) => {
    if (sectionId === activeSection && !mobile) return;
    if (hasUnsavedSettingsChanges) {
      setPendingSection(sectionId);
      return;
    }
    setActiveSection(sectionId);
    if (mobile) setMobileView("section");
  };

  const discardChangesAndSwitchSection = () => {
    if (!pendingSection) return;
    resetSettingsFormState();
    setActiveSection(pendingSection);
    setPendingSection(null);
    setMobileView("section");
  };

  const handleCopyInviteLink = async () => {
    if (!season?.inviteCode) return;
    try {
      await navigator.clipboard.writeText(inviteUrl);
      setCopied(true);
      toast.success("Invite link copied to clipboard!");
      setTimeout(() => setCopied(false), 2000);
    } catch {
      toast.error("Failed to copy invite link");
    }
  };

  const handleSportToggle = (sportId: string) => {
    setSelectedSports((prev) => {
      const next = new Set(prev);
      if (next.has(sportId)) {
        next.delete(sportId);
      } else {
        next.add(sportId);
      }
      return next;
    });
  };

  const sectionGridData: SettingsGridSection[] = [
    { id: "league-basics", label: "League Basics", icon: Shield, subtitle: "Name, teams, join" },
    { id: "draft-setup", label: "Draft Settings", icon: Swords, subtitle: "Format, timing, rounds" },
    { id: "draft-order", label: "Draft Order", icon: ListOrdered, subtitle: "Pick order, randomize" },
    { id: "sports", label: "Sports", icon: Trophy, subtitle: `${selectedSports.size} season${selectedSports.size !== 1 ? "s" : ""} selected` },
    { id: "scoring", label: "Scoring", icon: TrendingUp, subtitle: "Place values, ties" },
    { id: "notifications", label: "Notifications", icon: Bell, subtitle: "Discord alerts" },
    { id: "people", label: "People", icon: Users, subtitle: `${teamsWithOwners} of ${teamCountValue} teams` },
    { id: "history-danger", label: "History & Danger", icon: AlertTriangle, subtitle: "Audit log, dissolve", isDanger: true },
  ];

  return (
    <div className="container mx-auto max-w-5xl px-4 py-6 sm:py-8">
      <div className={cn(mobileView === "section" && "hidden lg:block")}>
        <LeagueSettingsHeader leagueId={league.id} leagueName={league.name} />
      </div>

      {mobileView === "grid" && (
        <div className="lg:hidden">
          <SettingsMobileGridNav
            sections={sectionGridData}
            onSectionChange={(id) => handleSectionChange(id as SettingsSectionId, { mobile: true })}
          />
        </div>
      )}

      {mobileView === "section" && (
        <SettingsMobileSectionPill onShowGrid={() => setMobileView("grid")} />
      )}

      <div className={cn("grid gap-6 lg:grid-cols-[220px_minmax(0,1fr)]", mobileView === "grid" && "hidden lg:grid")}>
        <SettingsDesktopNav
          sections={sectionGridData}
          activeSection={activeSection}
          onSectionChange={(id) => handleSectionChange(id as SettingsSectionId)}
        />

        <main className="space-y-6">
          {showUpdateForm && (
            <Form method="post" noValidate className="space-y-6" onSubmit={() => setHasUnsavedSettingsChanges(false)}>
              <input type="hidden" name="intent" value="update" />

              <LeagueBasicsSection
                active={activeSection === "league-basics"}
                season={season ?? null}
                leagueName={leagueName}
                onLeagueNameChange={(v) => { setLeagueName(v); markDirty(); }}
                isPublicDraftBoard={isPublicDraftBoard}
                onIsPublicDraftBoardChange={(v) => { setIsPublicDraftBoard(v); markDirty(); }}
                teamCountValue={teamCountValue}
                onTeamCountValueChange={(v) => { setTeamCountValue(v); markDirty(); }}
                teamsWithOwners={teamsWithOwners}
                canEditTeamCount={canEditTeamCount}
                minTeamCount={minTeamCount}
                inviteUrl={inviteUrl}
                copied={copied}
                onCopyInviteLink={handleCopyInviteLink}
              />

              <DraftSetupSection
                active={activeSection === "draft-setup"}
                canEditDraftRounds={canEditDraftRounds}
                minRounds={minRounds}
                recommendedRounds={recommendedRounds}
                flexSpots={flexSpots}
                draftRounds={draftRounds}
                onDraftRoundsChange={(v) => { setDraftRounds(v); markDirty(); }}
                draftDate={draftDate}
                onDraftDateChange={(v) => { setDraftDate(v); markDirty(); }}
                draftTime={draftTime}
                onDraftTimeChange={(v) => { setDraftTime(v); markDirty(); }}
                autoStartDraft={autoStartDraft}
                onAutoStartDraftChange={(v) => { setAutoStartDraft(v); markDirty(); }}
                timerMode={timerMode}
                onTimerModeChange={(v) => { setTimerMode(v); markDirty(); }}
                draftSpeed={draftSpeed}
                onDraftSpeedChange={(v) => { setDraftSpeed(v); markDirty(); }}
                overnightMode={overnightMode}
                onOvernightModeChange={(v) => { setOvernightMode(v); markDirty(); }}
                overnightStart={overnightStart}
                onOvernightStartChange={(v) => { setOvernightStart(v); markDirty(); }}
                overnightEnd={overnightEnd}
                onOvernightEndChange={(v) => { setOvernightEnd(v); markDirty(); }}
                overnightTimezone={overnightTimezone}
                onOvernightTimezoneChange={(v) => { setOvernightTimezone(v); markDirty(); }}
                commishTimezone={commishTimezone}
              />

              <SportsSection
                active={activeSection === "sports"}
                canEditSports={canEditSports}
                selectedSports={selectedSports}
                displaySportsSeasons={displaySportsSeasons}
                onSportToggle={(id) => { handleSportToggle(id); markDirty(); }}
                onClearAll={() => { setSelectedSports(new Set()); markDirty(); }}
              />

              <ScoringSection
                active={activeSection === "scoring"}
                canEditSports={canEditSports}
                season={season ?? null}
                scoringPreset={scoringPreset}
                onScoringPresetChange={(v) => { setScoringPreset(v); markDirty(); }}
                scoringRules={scoringRules}
                onScoringRulesChange={(v) => { setScoringRules(v); markDirty(); }}
              />

              <NotificationsSection
                active={activeSection === "notifications"}
                savedDiscordWebhookUrl={league.discordWebhookUrl}
                discordWebhookUrl={discordWebhookUrl}
                onDiscordWebhookUrlChange={(v) => { setDiscordWebhookUrl(v); markDirty(); }}
                discordPicksAnnouncementEnabled={discordPicksAnnouncementEnabled}
                onDiscordPicksAnnouncementEnabledChange={(v) => { setDiscordPicksAnnouncementEnabled(v); markDirty(); }}
              />

              <SettingsMessage actionData={actionData} />

              <SettingsSaveBar
                hasUnsavedChanges={hasUnsavedSettingsChanges}
                isSubmitting={isSubmittingSettings}
                onReset={resetSettingsFormState}
              />
            </Form>
          )}

          <LeaveSettingsDialog blocker={blocker} />

          <SwitchSettingsSectionDialog
            open={pendingSection !== null}
            onOpenChange={(open) => { if (!open) setPendingSection(null); }}
            onStay={() => setPendingSection(null)}
            onDiscard={discardChangesAndSwitchSection}
          />

          <DraftOrderSection
            active={activeSection === "draft-order"}
            draftOrderSet={draftOrderSet}
            canEditDraftOrder={canEditDraftOrder}
            hasUnsavedChanges={hasDraftOrderChanges}
            draftOrderTeams={draftOrderTeams}
            setDraftOrderTeams={setDraftOrderTeams}
            onDirtyChange={setHasDraftOrderChanges}
            onResetChanges={resetDraftOrder}
            teams={teams}
            ownerMap={ownerMap}
            navigationState={navigation.state}
            updateIntent={updateIntent}
            actionData={actionData}
          />

          <PeopleSection
            active={activeSection === "people"}
            teams={teams}
            ownerMap={ownerMap}
            commissioners={commissioners}
            currentUserId={currentUserId}
            teamCountValue={teamCountValue}
            teamsWithOwners={teamsWithOwners}
            leagueMembers={leagueMembers}
            isAdmin={isAdmin}
            navigationState={navigation.state}
          />

          {league.discordWebhookUrl && activeSection === "notifications" && (
            <Form method="post" className="rounded-lg border bg-card p-4">
              <input type="hidden" name="intent" value="test-discord-webhook" />
              <input type="hidden" name="webhookUrl" value={league.discordWebhookUrl} />
              <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
                <div>
                  <p className="font-medium">Test Discord notification</p>
                  <p className="text-sm text-muted-foreground">Sends a sample standings update to the saved webhook.</p>
                </div>
                <Button
                  type="submit"
                  variant="outline"
                  disabled={navigation.state === "submitting" && updateIntent === "test-discord-webhook"}
                >
                  Send Test
                </Button>
              </div>
            </Form>
          )}

              <HistoryDangerSection
                active={activeSection === "history-danger"}
                league={{ id: league.id, name: league.name }}
                season={season ?? null}
                isAdmin={isAdmin}
            isDeleteDialogOpen={isDeleteDialogOpen}
            onDeleteDialogOpenChange={setIsDeleteDialogOpen}
            navigationState={navigation.state}
          />
        </main>
      </div>
    </div>
  );
}
