import { database } from "~/database/context";
import * as schema from "~/database/schema";
import { eq, and, inArray } from "drizzle-orm";

export interface UpsertSeasonResultData {
  participantId: string;
  sportsSeasonId: string;
  currentPoints?: number;
  currentPosition?: number;
}

export interface UpdateSeasonResultData {
  currentPoints?: number;
  currentPosition?: number;
}

/**
 * Upsert a participant's season result (for F1, etc.)
 * Creates if doesn't exist, updates if it does
 */
export async function upsertParticipantSeasonResult(
  data: UpsertSeasonResultData,
  providedDb?: ReturnType<typeof database>
) {
  const db = providedDb || database();

  // Check if result exists
  const existing = await db.query.participantSeasonResults.findFirst({
    where: and(
      eq(schema.participantSeasonResults.participantId, data.participantId),
      eq(schema.participantSeasonResults.sportsSeasonId, data.sportsSeasonId)
    ),
  });

  if (existing) {
    // Update existing
    const [updated] = await db
      .update(schema.participantSeasonResults)
      .set({
        currentPoints: data.currentPoints?.toString(),
        currentPosition: data.currentPosition,
        updatedAt: new Date(),
      })
      .where(eq(schema.participantSeasonResults.id, existing.id))
      .returning();

    return updated;
  } else {
    // Insert new
    const [created] = await db
      .insert(schema.participantSeasonResults)
      .values({
        participantId: data.participantId,
        sportsSeasonId: data.sportsSeasonId,
        currentPoints: data.currentPoints?.toString() || "0",
        currentPosition: data.currentPosition,
      })
      .returning();

    return created;
  }
}

/**
 * Bulk upsert season results for multiple participants
 * Useful for entering standings for an entire F1 grid at once
 */
export async function upsertSeasonResultsBulk(
  results: UpsertSeasonResultData[],
  providedDb?: ReturnType<typeof database>
) {
  const db = providedDb || database();

  const upserted = [];
  for (const data of results) {
    const result = await upsertParticipantSeasonResult(data, db);
    upserted.push(result);
  }

  return upserted;
}

/**
 * Get a participant's season result
 */
export async function getParticipantSeasonResult(
  participantId: string,
  sportsSeasonId: string,
  providedDb?: ReturnType<typeof database>
) {
  const db = providedDb || database();

  return await db.query.participantSeasonResults.findFirst({
    where: and(
      eq(schema.participantSeasonResults.participantId, participantId),
      eq(schema.participantSeasonResults.sportsSeasonId, sportsSeasonId)
    ),
    with: {
      participant: {
        with: {
          sportsSeason: {
            with: {
              sport: true,
            },
          },
        },
      },
    },
  });
}

/**
 * Get all season results for a sports season
 * Returns participants ordered by current position (or points if position not set)
 */
export async function getSeasonResults(
  sportsSeasonId: string,
  providedDb?: ReturnType<typeof database>
) {
  const db = providedDb || database();

  const results = await db.query.participantSeasonResults.findMany({
    where: eq(schema.participantSeasonResults.sportsSeasonId, sportsSeasonId),
    with: {
      participant: {
        with: {
          sportsSeason: {
            with: {
              sport: true,
            },
          },
        },
      },
    },
  });

  // Sort by position (nulls last), then by points descending
  return results.toSorted((a, b) => {
    if (a.currentPosition !== null && b.currentPosition !== null) {
      return a.currentPosition - b.currentPosition;
    }
    if (a.currentPosition !== null) return -1;
    if (b.currentPosition !== null) return 1;

    // Both null positions, sort by points
    const aPoints = parseFloat(a.currentPoints || "0");
    const bPoints = parseFloat(b.currentPoints || "0");
    return bPoints - aPoints;
  });
}

/**
 * Get season results for specific participants
 */
export async function getSeasonResultsForParticipants(
  sportsSeasonId: string,
  participantIds: string[],
  providedDb?: ReturnType<typeof database>
) {
  const db = providedDb || database();

  if (participantIds.length === 0) return [];

  return await db.query.participantSeasonResults.findMany({
    where: and(
      eq(schema.participantSeasonResults.sportsSeasonId, sportsSeasonId),
      inArray(schema.participantSeasonResults.participantId, participantIds)
    ),
    with: {
      participant: {
        with: {
          sportsSeason: {
            with: {
              sport: true,
            },
          },
        },
      },
    },
  });
}

/**
 * Update a participant's season result
 */
export async function updateParticipantSeasonResult(
  participantId: string,
  sportsSeasonId: string,
  data: UpdateSeasonResultData,
  providedDb?: ReturnType<typeof database>
) {
  const db = providedDb || database();

  const [updated] = await db
    .update(schema.participantSeasonResults)
    .set({
      currentPoints: data.currentPoints?.toString(),
      currentPosition: data.currentPosition,
      updatedAt: new Date(),
    })
    .where(
      and(
        eq(schema.participantSeasonResults.participantId, participantId),
        eq(schema.participantSeasonResults.sportsSeasonId, sportsSeasonId)
      )
    )
    .returning();

  return updated;
}

/**
 * Delete all season results for a sports season
 */
export async function deleteSeasonResults(
  sportsSeasonId: string,
  providedDb?: ReturnType<typeof database>
) {
  const db = providedDb || database();

  await db
    .delete(schema.participantSeasonResults)
    .where(eq(schema.participantSeasonResults.sportsSeasonId, sportsSeasonId));
}

/**
 * Check if a participant has a season result
 */
export async function hasParticipantSeasonResult(
  participantId: string,
  sportsSeasonId: string,
  providedDb?: ReturnType<typeof database>
): Promise<boolean> {
  const db = providedDb || database();

  const result = await db.query.participantSeasonResults.findFirst({
    where: and(
      eq(schema.participantSeasonResults.participantId, participantId),
      eq(schema.participantSeasonResults.sportsSeasonId, sportsSeasonId)
    ),
  });

  return !!result;
}
