import { PrismaClient } from "@prisma/client";

export type AwardKind = "standard" | "list";

export const prisma = new PrismaClient();

const awardInclude = {
  announcedBy: { select: { id: true, name: true } },
  nominees: { orderBy: [{ sortOrder: "asc" as const }, { name: "asc" as const }] },
  collections: {
    include: {
      collectedBy: { select: { id: true, name: true } },
      nominee: { select: { id: true, name: true } },
    },
  },
} as const;

export function normalizeAwardKind(input: unknown): AwardKind {
  const v = String(input ?? "")
    .trim()
    .toLowerCase()
    .replace(/[\s_-]+/g, "");
  if (
    ["list", "multi", "multiwinner", "top", "winners", "allwinners", "recipients", "people", "group"].includes(v)
  ) {
    return "list";
  }
  return "standard";
}

export function listWinnerLabel(count: number) {
  return count === 1 ? "1 winner" : `${count} winners`;
}

export function normalizeNomineeNames(input: unknown): string[] {
  const raw = Array.isArray(input)
    ? input.map((n) => String(n ?? "").trim())
    : String(input ?? "")
        .split(/[,;|\n]+/)
        .map((n) => n.trim());
  const seen = new Set<string>();
  const names: string[] = [];
  for (const name of raw) {
    if (!name) continue;
    const key = name.toLowerCase();
    if (seen.has(key)) continue;
    seen.add(key);
    names.push(name);
  }
  return names;
}

export function ensureWinnerAmongNominees(winnerName: string, nominees: string[]) {
  const winner = winnerName.trim();
  if (!winner) return nominees;
  if (nominees.some((n) => n.toLowerCase() === winner.toLowerCase())) return nominees;
  return [winner, ...nominees];
}

export function resolveAwardPeople(opts: {
  kind: AwardKind;
  winnerName: string;
  nominees: string[];
}): { kind: AwardKind; winnerName: string; people: string[] } {
  const kind = opts.kind;
  let people = normalizeNomineeNames(opts.nominees);
  let winnerName = opts.winnerName.trim();

  if (kind === "list") {
    if (winnerName && !/^\d+\s+winners?$/i.test(winnerName)) {
      people = normalizeNomineeNames([...people, winnerName]);
    }
    winnerName = listWinnerLabel(people.length);
    return { kind, winnerName, people };
  }

  people = ensureWinnerAmongNominees(winnerName, people);
  return { kind, winnerName, people };
}

type CollectionRow = {
  id: string;
  awardId: string;
  nomineeId: string;
  isActualRecipient: boolean;
  kcHandle: string;
  collectedByUserId: string;
  collectedAt: Date;
  updatedAt: Date;
  collectedBy: { id: string; name: string };
  nominee: { id: string; name: string };
};

export function serializeAward(award: {
  id: string;
  category: string;
  kind?: string | null;
  winnerName: string;
  status: string;
  sortOrder: number;
  announcedAt: Date | null;
  announcedById: string | null;
  announcedBy: { id: string; name: string } | null;
  corrected: boolean;
  correctionNote: string | null;
  version: number;
  createdAt: Date;
  updatedAt: Date;
  nominees: { id: string; name: string; sortOrder: number }[];
  collections: CollectionRow[];
}) {
  const kind: AwardKind = award.kind === "list" ? "list" : "standard";
  const nominees = [...award.nominees]
    .sort((a, b) => a.sortOrder - b.sortOrder || a.name.localeCompare(b.name))
    .map((n) => ({ id: n.id, name: n.name, sortOrder: n.sortOrder }));

  const collections = [...award.collections]
    .sort((a, b) => a.nominee.name.localeCompare(b.nominee.name))
    .map((c) => ({
      id: c.id,
      awardId: c.awardId,
      nomineeId: c.nomineeId,
      nomineeName: c.nominee.name,
      isActualRecipient: c.isActualRecipient,
      kcHandle: c.kcHandle,
      collectedByUserId: c.collectedByUserId,
      collectedByName: c.collectedBy.name,
      collectedAt: c.collectedAt.toISOString(),
      updatedAt: c.updatedAt.toISOString(),
    }));

  const winnerName =
    kind === "list" && (!award.winnerName || /^\d+\s+winners?$/i.test(award.winnerName))
      ? listWinnerLabel(nominees.length)
      : award.winnerName;

  return {
    id: award.id,
    category: award.category,
    kind,
    winnerName,
    recipientName: winnerName,
    nominees,
    status: award.status,
    sortOrder: award.sortOrder,
    announcedAt: award.announcedAt?.toISOString() ?? null,
    announcedById: award.announcedById,
    announcedByName: award.announcedBy?.name ?? null,
    corrected: award.corrected,
    correctionNote: award.correctionNote,
    version: award.version,
    createdAt: award.createdAt.toISOString(),
    updatedAt: award.updatedAt.toISOString(),
    collections,
    certificatesTotal: nominees.length,
    certificatesCollected: collections.length,
  };
}

export async function getAwardDto(id: string) {
  const award = await prisma.award.findUnique({
    where: { id },
    include: awardInclude,
  });
  return award ? serializeAward(award) : null;
}

export async function listAwardsDto() {
  const awards = await prisma.award.findMany({
    include: awardInclude,
    orderBy: [{ sortOrder: "asc" }, { createdAt: "asc" }],
  });
  return awards.map(serializeAward);
}

export async function getStats() {
  const [total, pending, announced, collected] = await Promise.all([
    prisma.award.count(),
    prisma.award.count({ where: { status: "pending" } }),
    prisma.award.count({ where: { status: "announced" } }),
    prisma.award.count({ where: { status: "collected" } }),
  ]);
  return { total, pending, announced, collected };
}

export async function getCeremony() {
  const state = await prisma.ceremonyState.upsert({
    where: { id: "default" },
    update: {},
    create: { id: "default", currentAwardId: null },
  });
  return {
    currentAwardId: state.currentAwardId,
    updatedAt: state.updatedAt.toISOString(),
  };
}

export { awardInclude };
