import { useLoaderData, Link } from "react-router";
import { eq, asc, and, inArray } from "drizzle-orm";
import { getAuth } from "@clerk/react-router/server";
import { database } from "~/database/context";
import * as schema from "~/database/schema";
import { DraftGrid } from "~/components/DraftGrid";
import { useDraftSocket } from "~/hooks/useDraftSocket";
import { useState, useEffect } from "react";
import { buildOwnerMap } from "~/lib/owner-map";
import { Button } from "~/components/ui/button";
import { ArrowLeft } from "lucide-react";
import { calculateFantasyPoints, calculateBracketPoints } from "~/models/scoring-rules";
import logomarkUrl from "../../../../public/logomark.svg?url";
import type { CoronaState } from "~/components/draft/DraftPickCell";
import type { Route } from "./+types/$leagueId.draft-board.$seasonId";

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

export async function loader(args: Route.LoaderArgs) {
  const { params } = args;
  const { leagueId, seasonId } = params;

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

  const db = database();

  // Get season details
  const season = await db.query.seasons.findFirst({
    where: eq(schema.seasons.id, seasonId),
    with: {
      league: true,
    },
  });

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

  // Validate that the season actually belongs to the league in the URL
  if (season.leagueId !== leagueId) {
    throw new Response("Season not found", { status: 404 });
  }

  // Check access: public boards are accessible to everyone without auth
  if (!season.league.isPublicDraftBoard) {
    // Not public - check if the user is a league member or commissioner
    const { userId } = await getAuth(args);

    if (!userId) {
      throw new Response("This draft board is not public", { status: 403 });
    }

    // Check if user is a commissioner
    const isCommissioner = await db.query.commissioners.findFirst({
      where: and(
        eq(schema.commissioners.leagueId, season.leagueId),
        eq(schema.commissioners.userId, userId)
      ),
    });

    // Check if user has a team in this season
    const hasTeam = await db.query.teams.findFirst({
      where: and(
        eq(schema.teams.seasonId, seasonId),
        eq(schema.teams.ownerId, userId)
      ),
    });

    if (!isCommissioner && !hasTeam) {
      throw new Response("You don't have access to this draft board", { status: 403 });
    }
  }

  // Get draft slots (draft order)
  const draftSlots = await db
    .select({
      id: schema.draftSlots.id,
      draftOrder: schema.draftSlots.draftOrder,
      team: schema.teams,
    })
    .from(schema.draftSlots)
    .innerJoin(schema.teams, eq(schema.draftSlots.teamId, schema.teams.id))
    .where(eq(schema.draftSlots.seasonId, seasonId))
    .orderBy(asc(schema.draftSlots.draftOrder));

  // Get all draft picks with participant and sport info
  const draftPicks = await db
    .select({
      id: schema.draftPicks.id,
      pickNumber: schema.draftPicks.pickNumber,
      round: schema.draftPicks.round,
      pickInRound: schema.draftPicks.pickInRound,
      team: schema.teams,
      participant: schema.participants,
      sport: schema.sports,
      scoringPattern: schema.sportsSeasons.scoringPattern,
    })
    .from(schema.draftPicks)
    .innerJoin(schema.teams, eq(schema.draftPicks.teamId, schema.teams.id))
    .innerJoin(
      schema.participants,
      eq(schema.draftPicks.participantId, schema.participants.id)
    )
    .innerJoin(
      schema.sportsSeasons,
      eq(schema.participants.sportsSeasonId, schema.sportsSeasons.id)
    )
    .innerJoin(schema.sports, eq(schema.sportsSeasons.sportId, schema.sports.id))
    .where(eq(schema.draftPicks.seasonId, seasonId))
    .orderBy(asc(schema.draftPicks.pickNumber));

  const ownerMap = await buildOwnerMap(draftSlots);

  const coronaStates: Record<string, CoronaState> = {};

  if (draftPicks.length > 0) {
    const participantIds = draftPicks.map((p) => p.participant.id);
    const sportsSeasonIds = [
      ...new Set(draftPicks.map((p) => p.participant.sportsSeasonId)),
    ];

    const results = await db
      .select({
        participantId: schema.participantResults.participantId,
        sportsSeasonId: schema.participantResults.sportsSeasonId,
        finalPosition: schema.participantResults.finalPosition,
        isPartialScore: schema.participantResults.isPartialScore,
      })
      .from(schema.participantResults)
      .where(inArray(schema.participantResults.participantId, participantIds));

    const resultByParticipant = new Map(
      results.map((r) => [r.participantId, r])
    );

    const maxPoints = season.pointsFor1st;

    const bracketTemplateBySportsSeason = new Map<string, string | null>();
    if (sportsSeasonIds.length > 0) {
      const events = await db
        .select({
          sportsSeasonId: schema.scoringEvents.sportsSeasonId,
          bracketTemplateId: schema.scoringEvents.bracketTemplateId,
        })
        .from(schema.scoringEvents)
        .where(inArray(schema.scoringEvents.sportsSeasonId, sportsSeasonIds));
      for (const ev of events) {
        if (!bracketTemplateBySportsSeason.has(ev.sportsSeasonId)) {
          bracketTemplateBySportsSeason.set(
            ev.sportsSeasonId,
            ev.bracketTemplateId
          );
        }
      }
    }

    const scoringRules = {
      pointsFor1st: season.pointsFor1st,
      pointsFor2nd: season.pointsFor2nd,
      pointsFor3rd: season.pointsFor3rd,
      pointsFor4th: season.pointsFor4th,
      pointsFor5th: season.pointsFor5th,
      pointsFor6th: season.pointsFor6th,
      pointsFor7th: season.pointsFor7th,
      pointsFor8th: season.pointsFor8th,
    };

    for (const pick of draftPicks) {
      const result = resultByParticipant.get(pick.participant.id);

      if (!result || result.finalPosition === null) {
        coronaStates[pick.participant.id] = { type: "pending" };
        continue;
      }

      if (result.finalPosition === 0 && !result.isPartialScore) {
        coronaStates[pick.participant.id] = { type: "eliminated", points: 0 };
        continue;
      }

      if (result.finalPosition > 0) {
        const isBracket =
          pick.scoringPattern === "playoff_bracket";
        const templateId = isBracket
          ? bracketTemplateBySportsSeason.get(
              pick.participant.sportsSeasonId
            ) ?? null
          : null;
        const points = isBracket
          ? calculateBracketPoints(
              result.finalPosition,
              scoringRules,
              templateId
            )
          : calculateFantasyPoints(result.finalPosition, scoringRules);
        const brightness =
          maxPoints > 0 ? Math.min(points / maxPoints, 1) : 0;
        coronaStates[pick.participant.id] = {
          type: "scored",
          brightness,
          points,
        };
        continue;
      }

      coronaStates[pick.participant.id] = { type: "pending" };
    }
  }

  return {
    season,
    draftSlots,
    draftPicks,
    ownerMap,
    coronaStates,
  };
}

export default function DraftBoard() {
  const { season, draftSlots, draftPicks: initialPicks, ownerMap, coronaStates } = useLoaderData<typeof loader>();
  const { isConnected, on, off } = useDraftSocket(season.id);
  const [picks, setPicks] = useState(initialPicks);
  const [currentPick, setCurrentPick] = useState(season.currentPickNumber || 1);

  // Listen for new picks (only if draft is still active)
  useEffect(() => {
    if (season.status !== "draft") return;

    type PickShape = (typeof initialPicks)[number];
    const handlePickMade = (data: unknown) => {
      const pickData = data as { pick: PickShape; nextPickNumber: number };
      setPicks((prev) => [...prev, pickData.pick]);
      setCurrentPick(pickData.nextPickNumber);
    };

    on("pick-made", handlePickMade);

    return () => {
      off("pick-made", handlePickMade);
    };
  }, [on, off, season.status]);

  // Generate draft grid
  const totalTeams = draftSlots.length;
  const totalRounds = season.draftRounds || 1;
  type PickItem = (typeof initialPicks)[number];
  const draftGrid: Array<Array<PickItem | null>> = [];

  for (let round = 0; round < totalRounds; round++) {
    const roundPicks: Array<PickItem | null> = [];
    for (let teamIndex = 0; teamIndex < totalTeams; teamIndex++) {
      const pickNumber = round * totalTeams + teamIndex + 1;
      const pick = picks.find((p) => p.pickNumber === pickNumber);
      roundPicks.push(pick || null);
    }
    draftGrid.push(roundPicks);
  }

  const isDraftActive = season.status === "draft";
  const currentRound = totalTeams > 0 ? Math.ceil(currentPick / totalTeams) : 1;

  return (
    <div className="min-h-screen bg-background">
      <div className="border-b bg-card sticky top-0 z-10">
        <div className="w-full px-4 py-3">
          <div className="flex items-center justify-between">
            <div className="flex items-center gap-3">
              <Link to="/">
                <img src={logomarkUrl} alt="Brackt" className="h-8" />
              </Link>
              <h1 className="text-2xl font-bold">
                {season.league.name} Draft Board
              </h1>
            </div>
            <div className="flex items-center gap-4">
              {isDraftActive && (
                <div className="flex items-center gap-3 text-sm text-muted-foreground">
                  <span>Round {currentRound}</span>
                  <span>Pick {currentPick}</span>
                  <div className="flex items-center gap-2">
                    <div
                      className={`w-2.5 h-2.5 rounded-full ${
                        isConnected ? "bg-emerald-500" : "bg-coral-accent"
                      }`}
                    />
                    <span className="font-medium">
                      {isConnected ? "Connected" : "Disconnected"}
                    </span>
                  </div>
                </div>
              )}
              <Button variant="outline" size="sm" asChild>
                <Link to={`/leagues/${season.leagueId}`}>
                  <ArrowLeft className="mr-1.5 h-4 w-4" />
                  Back to League
                </Link>
              </Button>
            </div>
          </div>
        </div>
      </div>

      {/* Draft Grid */}
      <div className="w-full px-4 py-4">
        <DraftGrid
          draftSlots={draftSlots}
          draftGrid={draftGrid}
          currentPick={currentPick}
          ownerMap={ownerMap}
          coronaStates={coronaStates}
          seasonStatus={season.status}
          draftPaused={season.draftPaused}
        />
      </div>
    </div>
  );
}
