import { redirect, useLoaderData, useRevalidator, Link, useParams } from "react-router";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { ArrowLeft, ListOrdered, Users } from "lucide-react";
import { toast } from "sonner";
import { auth } from "~/lib/auth.server";
import { findSeasonWithTeamsAndLeague } from "~/models/season";
import { getDraftParticipants } from "~/models/season-participant";
import { getTeamQueue } from "~/models/draft-queue";
import { QueueSection } from "~/components/draft/QueueSection";
import { Button } from "~/components/ui/button";
import { Badge } from "~/components/ui/badge";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/components/ui/tabs";
import type { Route } from "./+types/$leagueId.draft-queue.$seasonId";

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

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

  if (!seasonId) {
    throw new Response("Season ID is required", { status: 400 });
  }

  const season = await findSeasonWithTeamsAndLeague(seasonId);

  if (!season) {
    throw new Response("Season not found", { status: 404 });
  }

  // Redirect if draft has started — send them to the appropriate page
  if (season.status === "draft") {
    return redirect(`/leagues/${leagueId}/draft/${seasonId}`);
  }
  if (season.status === "active" || season.status === "completed") {
    return redirect(`/leagues/${leagueId}/draft-board/${seasonId}`);
  }

  if (!userId) {
    throw new Response("You must be logged in to set pre-draft queue", { status: 401 });
  }

  const userTeam = season.teams.find((t) => t.ownerId === userId);
  if (!userTeam) {
    throw new Response("You do not have a team in this season", { status: 403 });
  }

  const [availableParticipants, userQueue] = await Promise.all([
    getDraftParticipants(seasonId),
    getTeamQueue(userTeam.id),
  ]);

  return {
    season,
    userTeam,
    availableParticipants,
    userQueue,
  };
}

type QueueItem = { id: string; participantId: string };

export default function SetPreDraftQueue() {
  const { season, userTeam, availableParticipants, userQueue } =
    useLoaderData<typeof loader>();
  const { leagueId } = useParams<{ leagueId: string }>();
  const { revalidate } = useRevalidator();

  const [localQueue, setLocalQueue] = useState<QueueItem[]>(userQueue);

  // Sync local state when server data refreshes
  useEffect(() => {
    setLocalQueue(userQueue);
  }, [userQueue]);

  // Always-current ref so reorder callback stays stable (no localQueue dependency)
  const queueRef = useRef(localQueue);
  // Track in-flight removes to prevent add racing ahead of a pending remove
  const pendingRemovesRef = useRef(new Set<string>());

  useEffect(() => {
    queueRef.current = localQueue;
  }, [localQueue]);

  const queuedParticipantIds = useMemo(
    () => new Set(localQueue.map((q) => q.participantId)),
    [localQueue]
  );

  const handleAdd = useCallback(
    async (participantId: string) => {
      if (queuedParticipantIds.has(participantId)) return;
      if (pendingRemovesRef.current.has(participantId)) {
        toast("Player is being removed — try again in a moment");
        return;
      }
      const tempId = `temp-${Date.now()}-${participantId}`;
      setLocalQueue((prev) => [...prev, { id: tempId, participantId }]);

      const formData = new FormData();
      formData.append("seasonId", season.id);
      formData.append("teamId", userTeam.id);
      formData.append("participantId", participantId);

      try {
        const res = await fetch("/api/queue/add", { method: "POST", body: formData });
        const data = await res.json();
        if (data.error) {
          setLocalQueue((prev) => prev.filter((item) => item.id !== tempId));
          toast.error(data.error || "Failed to add to rankings");
          return;
        }
        revalidate(); // swap temp ID for the real server-assigned ID
      } catch {
        setLocalQueue((prev) => prev.filter((item) => item.id !== tempId));
        toast.error("Network error — failed to add to rankings");
      }
    },
    [queuedParticipantIds, season.id, userTeam.id, revalidate]
  );

  const handleRemove = useCallback(
    async (queueId: string) => {
      const item = queueRef.current.find((q) => q.id === queueId);
      const participantId = item?.participantId;

      setLocalQueue((prev) => prev.filter((q) => q.id !== queueId));

      // Temp IDs haven't been written to the DB yet — nothing to delete server-side.
      if (queueId.startsWith("temp-")) return;

      if (participantId) pendingRemovesRef.current.add(participantId);

      const formData = new FormData();
      formData.append("queueId", queueId);
      formData.append("teamId", userTeam.id);

      try {
        const res = await fetch("/api/queue/remove", { method: "POST", body: formData });
        const data = await res.json();
        if (data.error) {
          toast.error(data.error || "Failed to remove from rankings");
          revalidate();
        }
      } catch {
        toast.error("Network error — failed to remove from rankings");
        revalidate();
      } finally {
        if (participantId) pendingRemovesRef.current.delete(participantId);
      }
    },
    [userTeam.id, revalidate]
  );

  // Remove by participantId — used from the All Players list
  const handleRemoveByParticipantId = useCallback(
    (participantId: string) => {
      const item = queueRef.current.find((q) => q.participantId === participantId);
      if (item) handleRemove(item.id);
    },
    [handleRemove]
  );

  const handleReorder = useCallback(
    async (participantIds: string[]) => {
      const previousQueue = queueRef.current;
      setLocalQueue((prev) => {
        const byParticipantId = new Map(prev.map((item) => [item.participantId, item]));
        return participantIds
          .map((pid) => byParticipantId.get(pid))
          .filter((item): item is QueueItem => item !== undefined);
      });

      const formData = new FormData();
      formData.append("teamId", userTeam.id);
      formData.append("seasonId", season.id);
      formData.append("participantIds", JSON.stringify(participantIds));

      try {
        const res = await fetch("/api/queue/reorder", { method: "POST", body: formData });
        const data = await res.json();
        if (data.error) {
          setLocalQueue(previousQueue);
          toast.error(data.error || "Failed to reorder rankings");
        }
      } catch {
        setLocalQueue(previousQueue);
        toast.error("Network error — failed to reorder rankings");
      }
    },
    [userTeam.id, season.id]
  );

  // ── Shared panel content ────────────────────────────────────────────────────

  const allPlayersPanel = useMemo(
    () => (
      <div className="space-y-1">
        {availableParticipants.map((p) => {
          const inQueue = queuedParticipantIds.has(p.id);
          return (
            <div
              key={p.id}
              className="flex items-center gap-3 rounded-lg bg-muted/50 px-3 py-2"
            >
              <div className="min-w-0 flex-1">
                <p className="font-medium text-sm truncate">{p.name}</p>
                <p className="text-xs text-muted-foreground">{p.sport.name}</p>
              </div>
              {inQueue ? (
                <Button
                  size="sm"
                  variant="ghost"
                  onClick={() => handleRemoveByParticipantId(p.id)}
                  className="h-7 text-xs shrink-0 text-destructive hover:text-destructive hover:bg-destructive/10"
                >
                  Remove
                </Button>
              ) : (
                <Button
                  size="sm"
                  variant="outline"
                  onClick={() => handleAdd(p.id)}
                  className="h-7 text-xs shrink-0"
                >
                  + Add
                </Button>
              )}
            </div>
          );
        })}
        {availableParticipants.length === 0 && (
          <p className="text-sm text-muted-foreground text-center py-12">
            No participants have been added to this season yet.
          </p>
        )}
      </div>
    ),
    [availableParticipants, queuedParticipantIds, handleAdd, handleRemoveByParticipantId]
  );

  const rankingsPanel = useMemo(
    () => (
      <>
        <div className="rounded-lg border bg-card">
          <QueueSection
            queue={localQueue}
            availableParticipants={availableParticipants}
            canPick={false}
            onRemoveFromQueue={handleRemove}
            onReorder={handleReorder}
            emptyMessage="Add players from All Players to set your ranking order."
          />
        </div>
        {localQueue.length > 0 && (
          <p className="text-xs text-muted-foreground mt-2 text-center">
            Drag to reorder · Autopick follows this order
          </p>
        )}
      </>
    ),
    [localQueue, availableParticipants, handleRemove, handleReorder]
  );

  // ── Render ──────────────────────────────────────────────────────────────────

  return (
    <div className="container mx-auto py-8 px-4">
      {/* Header */}
      <div className="mb-6">
        <Link
          to={`/leagues/${leagueId}`}
          className="inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground mb-3"
        >
          <ArrowLeft className="h-4 w-4" />
          Back to {season.league.name}
        </Link>
        <div className="flex items-center gap-2 mb-1">
          <ListOrdered className="h-6 w-6" />
          <h1 className="text-2xl font-bold">Set Pre-Draft Queue</h1>
        </div>
        <p className="text-sm text-muted-foreground">
          Rank the players you want. Your rankings carry into the live draft as your autopick
          priority order.
        </p>
      </div>

      {/* Mobile: tabs */}
      <div className="lg:hidden">
        <Tabs defaultValue="players">
          <TabsList className="grid w-full grid-cols-2 mb-4">
            <TabsTrigger value="players" className="flex items-center gap-1.5 text-xs">
              <Users className="h-4 w-4" />
              All Players
            </TabsTrigger>
            <TabsTrigger value="rankings" className="flex items-center gap-1.5 text-xs">
              <ListOrdered className="h-4 w-4" />
              My Rankings
              {localQueue.length > 0 && (
                <Badge variant="secondary" className="ml-1 h-4 px-1 text-[10px]">
                  {localQueue.length}
                </Badge>
              )}
            </TabsTrigger>
          </TabsList>
          <TabsContent value="players">
            {allPlayersPanel}
          </TabsContent>
          <TabsContent value="rankings">{rankingsPanel}</TabsContent>
        </Tabs>
      </div>

      {/* Desktop: side-by-side */}
      <div className="hidden lg:grid gap-6 lg:grid-cols-3">
        <div className="lg:col-span-2">
          <div className="mb-3">
            <h2 className="font-semibold">All Players</h2>
          </div>
          {allPlayersPanel}
        </div>
        <div>
          <div className="flex items-center justify-between mb-3">
            <h2 className="font-semibold">My Rankings</h2>
            <Badge variant="secondary">{localQueue.length}</Badge>
          </div>
          {rankingsPanel}
        </div>
      </div>
    </div>
  );
}
