import type { Route } from "./+types/admin.sports-seasons.$id.events.$eventId";
import { logger } from "~/lib/logger";
import { findSportsSeasonById } from "~/models/sports-season";
import { findParticipantsBySportsSeasonId, createParticipant } from "~/models/season-participant";
import {
  getScoringEventById,
  completeScoringEvent,
  updateScoringEvent,
} from "~/models/scoring-event";
import {
  getEventResults,
  createEventResult,
  createEventResultsBulk,
  updateEventResult,
  deleteEventResult,
  getEventResultById,
  getNotParticipatingResults,
  type CreateEventResultData,
  type UpdateEventResultData,
} from "~/models/event-result";
import { findParticipantsWithoutResults } from "./admin.sports-seasons.$id.events.$eventId.helpers";
import { findParticipantResultsBySportsSeasonId } from "~/models/participant-result";
import {
  upsertParticipantSeasonResult,
  getSeasonResults,
} from "~/models/participant-season-result";
import { processSeasonStandings, processQualifyingEvent } from "~/models/scoring-calculator";
import { getQPStandings, getQPConfig } from "~/models/qualifying-points";
import { database } from "~/database/context";
import * as schema from "~/database/schema";
import { eq } from "drizzle-orm";
import { upsertTournamentResult } from "~/models/tournament-result";
import { syncTournamentResults } from "~/services/sync-tournament-results";

export async function loader({ params }: Route.LoaderArgs) {
  const sportsSeason = await findSportsSeasonById(params.id);

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

  const event = await getScoringEventById(params.eventId);

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

  const participants = await findParticipantsBySportsSeasonId(params.id);
  const results = await getEventResults(params.eventId);
  const participantResults = await findParticipantResultsBySportsSeasonId(params.id);

  // For final_standings events, also get season results
  let seasonResults = null;
  if (event.eventType === "final_standings") {
    seasonResults = await getSeasonResults(params.id);
  }

  // For qualifying events, get QP config
  let qpConfig = null;
  if (event.isQualifyingEvent) {
    qpConfig = await getQPConfig(params.id);
  }

  // For qualifying sports seasons, get QP standings
  let qpStandings = null;
  if (sportsSeason.scoringPattern === "qualifying_points") {
    qpStandings = await getQPStandings(params.id);
  }

  // For qualifying major_tournament events, get not-participating participant IDs
  const notParticipatingIds: Set<string> =
    event.isQualifyingEvent && event.eventType === "major_tournament"
      ? new Set(
          (await getNotParticipatingResults(params.eventId)).map(
            (r) => r.seasonParticipantId
          )
        )
      : new Set();

  return {
    sportsSeason: sportsSeason as typeof sportsSeason & {
      sport: { id: string; name: string; type: string; slug: string; simulatorType: string | null };
    },
    event,
    participants,
    results,
    participantResults,
    seasonResults,
    qpConfig,
    qpStandings,
    notParticipatingIds,
  };
}

export async function action({ request, params }: Route.ActionArgs) {
  const formData = await request.formData();
  const intent = formData.get("intent");

  if (intent === "mark-qualifying") {
    try {
      const event = await getScoringEventById(params.eventId);
      if (!event) {
        return { error: "Event not found" };
      }

      // Update the event to mark it as a qualifying event
      const db = database();
      await db.update(schema.scoringEvents)
        .set({ isQualifyingEvent: true })
        .where(eq(schema.scoringEvents.id, params.eventId));

      return { success: "Event marked as qualifying event!" };
    } catch (error) {
      logger.error("Error marking event as qualifying:", error);
      return { error: "Failed to mark event as qualifying" };
    }
  }

  if (intent === "process-qp") {
    try {
      await processQualifyingEvent(params.eventId);
      return { success: "Qualifying points processed and awarded!" };
    } catch (error) {
      logger.error("Error processing qualifying points:", error);
      return { error: error instanceof Error ? error.message : "Failed to process qualifying points" };
    }
  }

  if (intent === "complete") {
    try {
      const event = await getScoringEventById(params.eventId);
      if (!event) {
        return { error: "Event not found" };
      }

      await completeScoringEvent(params.eventId);

      // If this is a final_standings event, process season standings
      if (event.eventType === "final_standings") {
        await processSeasonStandings(params.id);
        return { success: "Event completed and fantasy placements assigned to top 8!" };
      }

      // If this is a qualifying event, process qualifying points then fill 0-QP rows
      if (event.isQualifyingEvent) {
        await processQualifyingEvent(params.eventId);

        // Write 0-QP rows for all season participants who have no result for this event.
        // This marks them as "competed, earned nothing" so simulations skip this event.
        const allParticipants = await findParticipantsBySportsSeasonId(params.id);
        const existingResults = await getEventResults(params.eventId);
        const existingIds = new Set(existingResults.map((r) => r.seasonParticipantId));
        const missingIds = findParticipantsWithoutResults(
          allParticipants.map((p) => p.id),
          existingIds
        );

        if (missingIds.length > 0) {
          await createEventResultsBulk(
            missingIds.map((participantId) => ({
              scoringEventId: params.eventId,
              participantId,
              qualifyingPointsAwarded: 0,
            }))
          );
        }

        return { success: "Event completed and qualifying points awarded!" };
      }

      return { success: "Event marked as completed" };
    } catch (error) {
      logger.error("Error completing event:", error);
      return { error: "Failed to complete event" };
    }
  }

  if (intent === "uncomplete") {
    try {
      await updateScoringEvent(params.eventId, { isComplete: false });
      return { success: "Event marked as not updated" };
    } catch (error) {
      logger.error("Error uncompleting event:", error);
      return { error: "Failed to update event" };
    }
  }

  if (intent === "update-event") {
    const name = formData.get("name");
    const eventStartsAtRaw = formData.get("eventStartsAt");

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

    // eventStartsAt is a UTC ISO string from the client; empty string means "clear it"
    const eventStartsAt: Date | null | undefined =
      typeof eventStartsAtRaw === "string"
        ? eventStartsAtRaw
          ? new Date(eventStartsAtRaw)
          : null
        : undefined;

    // Derive eventDate from eventStartsAt; null when cleared
    const eventDate: Date | null | undefined =
      eventStartsAt !== undefined
        ? eventStartsAt
          ? new Date(eventStartsAt.toISOString().split("T")[0])
          : null
        : undefined;

    try {
      await updateScoringEvent(params.eventId, {
        name: name.trim(),
        eventDate,
        eventStartsAt,
      });
      return { success: "Event updated" };
    } catch (error) {
      logger.error("Error updating event:", error);
      return { error: "Failed to update event" };
    }
  }

  if (intent === "add-result") {
    const participantId = formData.get("participantId");
    const placement = formData.get("placement");

    if (typeof participantId !== "string" || !participantId) {
      return { error: "Participant is required" };
    }

    if (typeof placement !== "string" || !placement) {
      return { error: "Placement is required" };
    }

    const placementNum = parseInt(placement, 10);
    if (isNaN(placementNum) || placementNum < 1 || placementNum > 100) {
      return { error: "Placement must be between 1 and 100" };
    }

    const resultData: CreateEventResultData = {
      scoringEventId: params.eventId,
      participantId,
      placement: placementNum,
    };

    try {
      await createEventResult(resultData);
      return { success: "Result added successfully" };
    } catch (error) {
      logger.error("Error adding result:", error);
      return { error: "Failed to add result" };
    }
  }

  if (intent === "update-result") {
    const resultId = formData.get("resultId");
    const placement = formData.get("placement");

    if (typeof resultId !== "string" || !resultId) {
      return { error: "Result ID is required" };
    }

    if (typeof placement !== "string" || !placement) {
      return { error: "Placement is required" };
    }

    const placementNum = parseInt(placement, 10);
    if (isNaN(placementNum) || placementNum < 1 || placementNum > 100) {
      return { error: "Placement must be between 1 and 100" };
    }

    const updateData: UpdateEventResultData = {
      placement: placementNum,
    };

    try {
      await updateEventResult(resultId, updateData);
      return { success: "Result updated successfully" };
    } catch (error) {
      logger.error("Error updating result:", error);
      return { error: "Failed to update result" };
    }
  }

  if (intent === "delete-result") {
    const resultId = formData.get("resultId");

    if (typeof resultId !== "string" || !resultId) {
      return { error: "Result ID is required" };
    }

    try {
      await deleteEventResult(resultId);
      return { success: "Result deleted successfully" };
    } catch (error) {
      logger.error("Error deleting result:", error);
      return { error: "Failed to delete result" };
    }
  }

  if (intent === "update-standings") {
    // Bulk update season standings for final_standings events
    try {
      // Parse all form fields and collect participants with points
      const participantPoints: Array<{ participantId: string; points: number }> = [];

      for (const [key, value] of formData.entries()) {
        if (key.startsWith("points-")) {
          const participantId = key.replace("points-", "");
          const points = value ? parseFloat(value as string) : 0;

          if (points > 0) {
            participantPoints.push({ participantId, points });
          }
        }
      }

      // Sort by points descending to determine positions
      participantPoints.sort((a, b) => b.points - a.points);

      // Assign positions based on sorted order and update
      for (let i = 0; i < participantPoints.length; i++) {
        const { participantId, points } = participantPoints[i];
        const position = i + 1; // Position is 1-based

        await upsertParticipantSeasonResult(
          {
            participantId,
            sportsSeasonId: params.id,
            currentPoints: points,
            currentPosition: position,
          }
        );
      }

      // Also update participants with 0 or no points (they don't have a position)
      for (const [key, value] of formData.entries()) {
        if (key.startsWith("points-")) {
          const participantId = key.replace("points-", "");
          const points = value ? parseFloat(value as string) : 0;

          if (points === 0) {
            await upsertParticipantSeasonResult(
              {
                participantId,
                sportsSeasonId: params.id,
                currentPoints: 0,
                currentPosition: undefined,
              }
            );
          }
        }
      }

      return { success: `Updated standings for ${participantPoints.length} participants (positions auto-calculated from points)` };
    } catch (error) {
      logger.error("Error updating season standings:", error);
      return { error: "Failed to update season standings" };
    }
  }

  if (intent === "batch-add-results") {
    const resultsJson = formData.get("results");
    if (typeof resultsJson !== "string" || !resultsJson) {
      return { error: "Results data is required" };
    }

    let incoming: { participantId: string; placement: number }[];
    try {
      incoming = JSON.parse(resultsJson);
    } catch {
      return { error: "Invalid results format" };
    }

    if (!Array.isArray(incoming) || incoming.length === 0) {
      return { error: "No results provided" };
    }

    // Validate that all participant IDs belong to this sports season
    const allSeasonParticipants = await findParticipantsBySportsSeasonId(params.id);
    const validParticipantIds = new Set(allSeasonParticipants.map((p) => p.id));
    const invalidEntries = incoming.filter((r) => !validParticipantIds.has(r.participantId));
    if (invalidEntries.length > 0) {
      return { error: "Some participant IDs do not belong to this sports season" };
    }

    try {
      // If this scoring event is linked to a canonical tournament, write
      // results to the canonical layer and fan out via syncTournamentResults.
      // This keeps sibling windows (if any) in sync automatically.
      const scoringEvent = await database().query.scoringEvents.findFirst({
        where: eq(schema.scoringEvents.id, params.eventId),
      });
      if (!scoringEvent) {
        return { error: "Scoring event not found" };
      }

      // Qualifying events always have a canonical tournament link (check
      // constraint). Write results canonically and fan out.
      if (!scoringEvent.tournamentId) {
        return {
          error:
            "This scoring event has no canonical tournament link — cannot save batch results. Contact a developer.",
        };
      }

      const spRows = allSeasonParticipants.filter((sp) =>
        incoming.some((r) => r.participantId === sp.id),
      );
      const missingCanonical = spRows.filter((sp) => !sp.participantId);
      if (missingCanonical.length > 0) {
        return {
          error: `Some season participants are not linked to canonical participants: ${missingCanonical.map((sp) => sp.name).join(", ")}`,
        };
      }

      for (const row of incoming) {
        const sp = spRows.find((s) => s.id === row.participantId);
        if (!sp?.participantId) continue;
        await upsertTournamentResult({
          tournamentId: scoringEvent.tournamentId,
          participantId: sp.participantId,
          placement: row.placement,
        });
      }

      const syncReport = await syncTournamentResults(scoringEvent.tournamentId);
      return {
        success: `${incoming.length} result${incoming.length === 1 ? "" : "s"} saved canonically. Synced to ${syncReport.windowsSynced} window${syncReport.windowsSynced === 1 ? "" : "s"}${syncReport.windowsFailed > 0 ? ` (${syncReport.windowsFailed} failed)` : ""}.`,
        syncReport,
      };
    } catch (error) {
      logger.error("Error saving batch results:", error);
      return { error: "Failed to save results" };
    }
  }

  if (intent === "mark-not-participating") {
    const participantId = formData.get("participantId");

    if (typeof participantId !== "string" || !participantId) {
      return { error: "Participant ID is required" };
    }

    const event = await getScoringEventById(params.eventId);
    if (!event) return { error: "Event not found" };
    if (event.isComplete) return { error: "Cannot mark not-participating on a completed event" };

    try {
      await createEventResult({
        scoringEventId: params.eventId,
        participantId,
        notParticipating: true,
      });
      return { success: "Participant marked as not participating" };
    } catch (error) {
      if (error instanceof Error && error.message.includes("event_results_event_participant_unique")) {
        return { error: "This participant is already marked for this event" };
      }
      logger.error("Error marking not-participating:", error);
      return { error: "Failed to mark participant as not participating" };
    }
  }

  if (intent === "unmark-not-participating") {
    const resultId = formData.get("resultId");

    if (typeof resultId !== "string" || !resultId) {
      return { error: "Result ID is required" };
    }

    const result = await getEventResultById(resultId);
    if (!result) return { error: "Result not found" };
    if (result.scoringEvent.id !== params.eventId) return { error: "Result does not belong to this event" };
    if (!result.notParticipating) return { error: "Cannot remove a result that is not a not-participating marker" };

    try {
      await deleteEventResult(resultId);
      return { success: "Participant re-added to event" };
    } catch (error) {
      logger.error("Error unmarking not-participating:", error);
      return { error: "Failed to re-add participant to event" };
    }
  }

  if (intent === "create-participant") {
    const name = formData.get("name");

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

    try {
      const participant = await createParticipant({
        name: name.trim(),
        sportsSeasonId: params.id,
      });
      return {
        success: `Participant "${participant.name}" created.`,
        newParticipant: { id: participant.id, name: participant.name },
      };
    } catch (error) {
      logger.error("Error creating participant:", error);
      if (error instanceof Error && error.message.includes("participants_sports_season_name_unique")) {
        return { error: `A participant named "${name.trim()}" already exists in this sports season.` };
      }
      return { error: "Failed to create participant" };
    }
  }

  return { error: "Invalid action" };
}
