import { computed, reactive, ref, watch } from 'vue'

import { api } from '../../lib/api'
import { useAwardsStore } from '../../stores/awards'
import type { AdminAuditEntry, AdminSeasonDetailResponse } from '../../types/awards'
import { hasShowDayPassed, normalizePhaseKey } from './adminSeasonTimeline'
import type {
  AdminSeasonCopyOption,
  AdminSeasonCreateForm,
  AdminSeasonGatewayEntryLink,
  AdminSeasonGatewayCheckItem,
  AdminSeasonForm,
  AdminSeasonPhaseGateway,
  AdminSeasonReadinessItem,
} from './adminSeasonTypes'

function createEmptySeasonForm(): AdminSeasonForm {
  return {
    year: new Date().getFullYear(),
    name: '',
    currentPhase: '',
    isCurrent: false,
    isCommunityOnly: true,
    nominationStartsAt: '',
    nominationEndsAt: '',
    votingStartsAt: '',
    votingEndsAt: '',
    reviewStartsAt: '',
    reviewEndsAt: '',
    showDate: '',
    showStartsAt: '20:00',
  }
}

function createEmptyCreateForm(): AdminSeasonCreateForm {
  return {
    year: new Date().getFullYear() + 1,
    name: '',
    currentPhase: 'Nominierung',
    isCurrent: false,
    isCommunityOnly: true,
    nominationStartsAt: '',
    nominationEndsAt: '',
    votingStartsAt: '',
    votingEndsAt: '',
    reviewStartsAt: '',
    reviewEndsAt: '',
    showDate: '',
    showStartsAt: '20:00',
    copyStructureFromSeasonId: null,
  }
}

export function useAdminSeasonManager() {
  const store = useAwardsStore()
  const saving = ref(false)
  const adminMessage = ref('')
  const adminError = ref('')
  const createModalOpen = ref(false)
  const creating = ref(false)
  const seasonToDelete = ref<{ id: number; year: number; name: string; isCurrent: boolean } | null>(null)
  const deleting = ref(false)
  const completing = ref(false)
  const phaseGateway = ref<AdminSeasonPhaseGateway | null>(null)
  const seasonAuditEntries = ref<AdminAuditEntry[]>([])
  const loadingSeasonAudit = ref(false)
  let seasonAuditRequestId = 0

  const form = reactive(createEmptySeasonForm())
  const createForm = reactive(createEmptyCreateForm())

  const seasonDetail = computed(() => store.adminSeasonDetail)
  const selectedSeasonId = computed(() => store.adminSelectedSeasonId)
  const selectedSeason = computed(() =>
    store.adminSeasons.find((season) => season.id === selectedSeasonId.value) ?? null,
  )
  const phasePresets = ['Nominierung', 'Community Voting', 'Aufbereitung', 'Award Show', 'Abgeschlossen']
  const readinessItems = computed(() => buildReadinessItems(seasonDetail.value, form.currentPhase))
  const publicReadinessIssues = computed(() =>
    readinessItems.value
      .filter((item) => item.blocking && !item.complete)
      .map((item) => item.note),
  )
  const archiveReadinessIssues = computed(() => buildArchiveReadinessIssues(seasonDetail.value))
  const visibleArchiveReadinessIssues = computed(() =>
    normalizePhaseKey(form.currentPhase) === 'completed' ? archiveReadinessIssues.value : [],
  )
  const createPublicReadinessIssues = computed(() => buildCreatePublicReadinessIssues(createForm))
  const canActivatePublic = computed(() => publicReadinessIssues.value.length === 0)
  const canCompleteSelectedSeason = computed(() =>
    Boolean(
      selectedSeason.value &&
        normalizePhaseKey(form.currentPhase) !== 'completed',
    ),
  )
  const canDeleteSelectedSeason = computed(() => Boolean(selectedSeason.value && !selectedSeason.value.isCurrent))
  const copySourceOptions = computed<AdminSeasonCopyOption[]>(() => [
    { label: 'Keine Struktur kopieren', value: null },
    ...store.adminSeasons.map((season) => ({
      label: `${season.year} · ${season.name} (${season.categoryCount} Kategorien)`,
      value: season.id,
    })),
  ])
  const canCreate = computed(() =>
    Boolean(
      createForm.year &&
        createForm.name.trim() &&
        createForm.currentPhase.trim() &&
        createForm.nominationStartsAt &&
        createForm.nominationEndsAt &&
        createForm.votingStartsAt &&
        createForm.votingEndsAt &&
        createForm.reviewStartsAt &&
        createForm.reviewEndsAt &&
        createForm.showDate &&
        createForm.showStartsAt &&
        createPublicReadinessIssues.value.length === 0,
    ),
  )
  const latestSeasonAuditEntry = computed(() => seasonAuditEntries.value[0] ?? null)
  const latestSeasonAuditSummary = computed(() =>
    latestSeasonAuditEntry.value?.summary ?? 'Noch keine Aktion für dieses Jahr gefunden.',
  )
  const latestSeasonAuditMeta = computed(() => {
    const entry = latestSeasonAuditEntry.value
    return entry ? `${formatAuditDate(entry.createdAt)} · ${entry.adminTwitchUserId}` : 'Audit wird nach der ersten Aenderung angezeigt.'
  })

  watch(
    seasonDetail,
    (detail) => {
      form.year = detail.year
      form.name = detail.name
      form.currentPhase = detail.currentPhase
      form.isCurrent = detail.isCurrent
      form.isCommunityOnly = detail.isCommunityOnly
      form.nominationStartsAt = detail.nominationStartsAt
      form.nominationEndsAt = detail.nominationEndsAt
      form.votingStartsAt = detail.votingStartsAt
      form.votingEndsAt = detail.votingEndsAt
      form.reviewStartsAt = detail.reviewStartsAt
      form.reviewEndsAt = detail.reviewEndsAt
      form.showDate = detail.showDate
      form.showStartsAt = normalizeTimeInput(detail.showStartsAt)
    },
    { immediate: true },
  )

  watch(
    () => createForm.year,
    (year) => {
      if (year >= 2020 && (!createForm.name || createForm.name.startsWith('VTuber Star Awards '))) {
        createForm.name = `VTuber Star Awards ${year}`
      }
    },
  )

  watch(
    () => [selectedSeasonId.value, seasonDetail.value.year, seasonDetail.value.name] as const,
    () => {
      void loadSeasonAuditEntries()
    },
    { immediate: true },
  )

  function fillCreateDefaults(year = new Date().getFullYear() + 1) {
    createForm.year = year
    createForm.name = `VTuber Star Awards ${year}`
    createForm.currentPhase = 'Nominierung'
    createForm.isCurrent = false
    createForm.isCommunityOnly = true
    createForm.nominationStartsAt = `${year}-08-01`
    createForm.nominationEndsAt = `${year}-08-24`
    createForm.votingStartsAt = `${year}-08-25`
    createForm.votingEndsAt = `${year}-09-11`
    createForm.reviewStartsAt = `${year}-09-12`
    createForm.reviewEndsAt = `${year}-09-19`
    createForm.showDate = `${year}-09-20`
    createForm.showStartsAt = '20:00'
    createForm.copyStructureFromSeasonId = findCopySourceForYear(year)
  }

  function openCreateModal() {
    adminMessage.value = ''
    adminError.value = ''
    fillCreateDefaults(
      Math.max(new Date().getFullYear(), (store.adminSeasons[0]?.year ?? new Date().getFullYear()) + 1),
    )
    createModalOpen.value = true
  }

  async function persistSeason(successMessage: string) {
    if (!selectedSeasonId.value) {
      return false
    }

    const completesSeasonNow =
      normalizePhaseKey(form.currentPhase) === 'completed' &&
      normalizePhaseKey(seasonDetail.value.currentPhase) !== 'completed'
    if (completesSeasonNow && archiveReadinessIssues.value.length > 0) {
      adminMessage.value = ''
      adminError.value = `Abschluss blockiert: ${archiveReadinessIssues.value.join(' ')}`
      return false
    }

    if (form.isCurrent && publicReadinessIssues.value.length > 0) {
      adminMessage.value = ''
      adminError.value = `Public-Aktivierung blockiert: ${publicReadinessIssues.value.join(' ')}`
      return false
    }

    saving.value = true
    adminMessage.value = ''
    adminError.value = ''

    try {
      await store.updateAdminSeason(selectedSeasonId.value, normalizeSeasonPayload(form))
      await loadSeasonAuditEntries()
      adminMessage.value = successMessage
      return true
    } catch (error) {
      adminError.value = error instanceof Error ? error.message : 'Jahr konnte nicht gespeichert werden.'
      return false
    } finally {
      saving.value = false
    }
  }

  async function saveSeason() {
    return persistSeason('Jahresstatus gespeichert.')
  }

  async function activatePublicSeason() {
    if (!selectedSeasonId.value || saving.value || form.isCurrent) {
      return false
    }

    form.isCurrent = true
    const saved = await persistSeason(`Award-Jahr ${form.year} ist jetzt auf der Landingpage aktiv.`)
    if (!saved) {
      form.isCurrent = false
    }
    return saved
  }

  async function activatePhase(phase: string) {
    if (!selectedSeasonId.value || saving.value || form.currentPhase === phase) {
      return
    }

    const previousPhase = form.currentPhase
    form.currentPhase = phase
    const saved = await persistSeason(`Phase „${phase}“ wurde aktiviert.`)
    if (!saved) {
      form.currentPhase = previousPhase
    }
  }

  async function completeSeason() {
    if (!selectedSeasonId.value || completing.value) {
      return
    }

    completing.value = true
    adminMessage.value = ''
    adminError.value = ''

    try {
      if (archiveReadinessIssues.value.length > 0) {
        adminError.value = `Abschluss blockiert: ${archiveReadinessIssues.value.join(' ')}`
        return
      }

      const payload = {
        ...form,
        currentPhase: 'Abgeschlossen',
      }
      await store.updateAdminSeason(selectedSeasonId.value, normalizeSeasonPayload(payload))
      await loadSeasonAuditEntries()
      form.currentPhase = 'Abgeschlossen'
      adminMessage.value = `Award-Jahr ${form.year} wurde abgeschlossen.`
    } catch (error) {
      adminError.value = error instanceof Error ? error.message : 'Award-Jahr konnte nicht abgeschlossen werden.'
    } finally {
      completing.value = false
    }
  }

  async function createSeason() {
    if (!canCreate.value) {
      if (createPublicReadinessIssues.value.length > 0) {
        adminError.value = `Direkte Public-Aktivierung blockiert: ${createPublicReadinessIssues.value.join(' ')}`
      }
      return
    }

    creating.value = true
    adminMessage.value = ''
    adminError.value = ''

    try {
      await store.createAdminSeason(normalizeSeasonPayload(createForm))
      createModalOpen.value = false
      adminMessage.value = createForm.copyStructureFromSeasonId
        ? `Award-Jahr ${createForm.year} wurde mit kopierter Struktur angelegt.`
        : `Award-Jahr ${createForm.year} wurde angelegt.`
    } catch (error) {
      adminError.value = error instanceof Error ? error.message : 'Award-Jahr konnte nicht angelegt werden.'
    } finally {
      creating.value = false
    }
  }

  function requestPhaseGateway(phase: string) {
    const phaseKey = normalizePhaseKey(phase)
    if (phaseKey === 'voting') {
      const gateway = buildVotingGateway(seasonDetail.value)
      if (gateway.blockers.length > 0 || gateway.warnings.length > 0) {
        phaseGateway.value = gateway
        return
      }
    }

    void activatePhase(phase)
  }

  function requestCompletionGateway() {
    const gateway = buildCompletionGateway(seasonDetail.value, form)
    if (gateway.blockers.length > 0 || gateway.warnings.length > 0) {
      phaseGateway.value = gateway
      return
    }

    void completeSeason()
  }

  async function confirmPhaseGateway() {
    const gateway = phaseGateway.value
    if (!gateway) {
      return
    }

    phaseGateway.value = null
    if (gateway.action === 'complete') {
      await completeSeason()
      return
    }

    await activatePhase(gateway.phase)
  }

  function openDeleteSeasonModal() {
    if (!selectedSeason.value) {
      return
    }

    seasonToDelete.value = {
      id: selectedSeason.value.id,
      year: selectedSeason.value.year,
      name: selectedSeason.value.name,
      isCurrent: selectedSeason.value.isCurrent,
    }
  }

  async function confirmDeleteSeason() {
    if (!seasonToDelete.value || seasonToDelete.value.isCurrent) {
      return
    }

    deleting.value = true
    adminMessage.value = ''
    adminError.value = ''

    try {
      const deletedYear = seasonToDelete.value.year
      await store.deleteAdminSeason(seasonToDelete.value.id)
      await loadSeasonAuditEntries()
      seasonToDelete.value = null
      adminMessage.value = `Award-Jahr ${deletedYear} wurde gelöscht.`
    } catch (error) {
      adminError.value = error instanceof Error ? error.message : 'Award-Jahr konnte nicht gelöscht werden.'
    } finally {
      deleting.value = false
    }
  }

  function findCopySourceForYear(year: number) {
    const previousSeason = [...store.adminSeasons]
      .filter((season) => season.year < year)
      .sort((left, right) => right.year - left.year)[0]

    return previousSeason?.id ?? store.adminSeasons[0]?.id ?? null
  }

  async function loadSeasonAuditEntries() {
    const detail = seasonDetail.value
    if (!detail.id) {
      seasonAuditEntries.value = []
      return
    }

    const requestId = ++seasonAuditRequestId
    loadingSeasonAudit.value = true

    try {
      const entries = await api.getAdminAuditEntries(100, 'season')
      if (requestId !== seasonAuditRequestId) {
        return
      }

      const knownEntityIds = new Set([String(detail.id), String(detail.year)])
      seasonAuditEntries.value = entries
        .filter((entry) =>
          entry.entityType === 'season' &&
          knownEntityIds.has(entry.entityId) &&
          entry.actionType.startsWith('season.'),
        )
        .slice(0, 5)
    } catch {
      if (requestId === seasonAuditRequestId) {
        seasonAuditEntries.value = []
      }
    } finally {
      if (requestId === seasonAuditRequestId) {
        loadingSeasonAudit.value = false
      }
    }
  }

  return {
    store,
    form,
    createForm,
    saving,
    adminMessage,
    adminError,
    createModalOpen,
    creating,
    completing,
    seasonToDelete,
    deleting,
    seasonDetail,
    selectedSeasonId,
    selectedSeason,
    readinessItems,
    publicReadinessIssues,
    archiveReadinessIssues: visibleArchiveReadinessIssues,
    createPublicReadinessIssues,
    canActivatePublic,
    phasePresets,
    canDeleteSelectedSeason,
    canCompleteSelectedSeason,
    copySourceOptions,
    loadingSeasonAudit,
    latestSeasonAuditSummary,
    latestSeasonAuditMeta,
    canCreate,
    phaseGateway,
    activatePhase,
    activatePublicSeason,
    requestPhaseGateway,
    requestCompletionGateway,
    confirmPhaseGateway,
    openCreateModal,
    saveSeason,
    completeSeason,
    createSeason,
    openDeleteSeasonModal,
    confirmDeleteSeason,
  }
}

function normalizeTimeInput(value: string) {
  return value ? value.slice(0, 5) : '20:00'
}

function normalizeTimeForApi(value: string) {
  const normalized = normalizeTimeInput(value)
  return normalized.length === 5 ? `${normalized}:00` : normalized
}

function normalizeSeasonPayload<T extends { showStartsAt: string }>(payload: T): T {
  return {
    ...payload,
    showStartsAt: normalizeTimeForApi(payload.showStartsAt),
  }
}

function resolveSeasonFacts(detail: AdminSeasonDetailResponse) {
  const categoryIdsWithCandidates = new Set(
    detail.candidates
      .filter((candidate) => candidate.acceptanceStatus !== 'declined')
      .map((candidate) => candidate.categoryId),
  )
  const categoryIdsWithResults = new Set(detail.results.map((result) => result.categoryId))
  const emptyCategories = detail.categories.filter((category) => !categoryIdsWithCandidates.has(category.id)).length
  const missingResults = detail.categories.filter((category) => !categoryIdsWithResults.has(category.id)).length
  const pendingClips = detail.clipSubmissions.filter((clip) => clip.status === 'pending').length

  return {
    emptyCategories,
    missingResults,
    pendingClips,
  }
}

function buildReadinessItems(
  detail: AdminSeasonDetailResponse,
  currentPhase: string,
): AdminSeasonReadinessItem[] {
  const facts = resolveSeasonFacts(detail)
  const activeCandidateCount = detail.candidates.filter((candidate) => candidate.acceptanceStatus !== 'declined').length
  const phaseKey = normalizePhaseKey(currentPhase)
  const candidateBlocking = phaseKey !== 'nomination'
  const winnerBlocking = phaseKey === 'completed'

  return [
    {
      label: 'Kategorien',
      note: detail.categories.length > 0
        ? `${detail.categories.length} Kategorien vorbereitet`
        : 'Mindestens eine Kategorie ist erforderlich.',
      complete: detail.categories.length > 0,
      blocking: true,
      to: '/admin/categories',
    },
    {
      label: 'Kandidatenbasis',
      note: facts.emptyCategories === 0 && activeCandidateCount > 0
        ? 'Alle Kategorien haben Kandidaten.'
        : candidateBlocking
          ? `${facts.emptyCategories} Kategorien brauchen noch Kandidaten.`
          : 'In der Nominierung dürfen Kategorien noch leer sein.',
      complete: detail.categories.length > 0 && facts.emptyCategories === 0 && activeCandidateCount > 0,
      blocking: candidateBlocking,
      to: '/admin/candidates',
    },
    {
      label: 'Reviews',
      note: detail.pendingNominations.length === 0
        ? 'Keine offenen Nominierungsreviews.'
        : `${detail.pendingNominations.length} Reviews sollten vor dem Voting-Freeze entschieden werden.`,
      complete: detail.pendingNominations.length === 0,
      blocking: false,
      to: '/admin/nominations?review=1',
    },
    {
      label: 'Clips',
      note: facts.pendingClips === 0
        ? 'Keine offenen Clip-Pruefungen.'
        : `${facts.pendingClips} Clips warten noch auf Pruefung.`,
      complete: facts.pendingClips === 0,
      blocking: false,
      to: '/admin/clips',
    },
    {
      label: 'Gewinner / Archiv',
      note: detail.categories.length > 0 && facts.missingResults === 0
        ? 'Alle Gewinner sind vergeben.'
        : winnerBlocking
          ? `${facts.missingResults} Kategorien brauchen vor Abschluss einen Gewinner.`
          : 'Vor Abschluss müssen alle Gewinner gesetzt sein.',
      complete: detail.categories.length > 0 && facts.missingResults === 0,
      blocking: winnerBlocking,
      to: '/admin/analytics',
    },
  ]
}

function buildArchiveReadinessIssues(detail: AdminSeasonDetailResponse) {
  const facts = resolveSeasonFacts(detail)
  const issues: string[] = []

  if (!hasShowDayPassed(detail)) {
    issues.push(
      detail.showDate
        ? 'Die Award Show liegt noch nicht in der Vergangenheit.'
        : 'Lege zuerst ein Datum für die Award Show fest.',
    )
  }

  if (detail.categories.length === 0) {
    issues.push('Mindestens eine Kategorie ist erforderlich.')
  }

  if (facts.emptyCategories > 0) {
    issues.push(`${facts.emptyCategories} Kategorien haben noch keine Kandidaten.`)
  }

  if (facts.missingResults > 0) {
    issues.push(`${facts.missingResults} Kategorien haben noch keinen Gewinner.`)
  }

  return issues
}

function buildCreatePublicReadinessIssues(createForm: AdminSeasonCreateForm) {
  if (!createForm.isCurrent) {
    return []
  }

  const phaseKey = normalizePhaseKey(createForm.currentPhase)
  const issues: string[] = []
  if (!createForm.copyStructureFromSeasonId) {
    issues.push('Kopiere erst eine Vorjahres-Struktur oder lege Kategorien nach dem Erstellen manuell an.')
  }

  if (phaseKey !== 'nomination') {
    issues.push('Direkt public ist für neue Jahre nur in der Nominierungsphase sinnvoll, weil Kandidaten noch fehlen.')
  }

  return issues
}

function buildVotingGateway(detail: AdminSeasonDetailResponse): AdminSeasonPhaseGateway {
  const blockers: AdminSeasonGatewayCheckItem[] = []
  const warnings: AdminSeasonGatewayCheckItem[] = []
  const categoryIdsWithCandidates = new Set(
    detail.candidates
      .filter((candidate) => candidate.acceptanceStatus !== 'declined')
      .map((candidate) => candidate.categoryId),
  )
  const emptyCategories = detail.categories.filter((category) => !categoryIdsWithCandidates.has(category.id))
  if (emptyCategories.length > 0) {
    blockers.push({
      label: 'Kategorien ohne voting-bereite Kandidaten',
      detail: emptyCategories.map((category) => category.name).slice(0, 4).join(', ')
        + (emptyCategories.length > 4 ? ` +${emptyCategories.length - 4} weitere` : ''),
      status: 'block',
      to: '/admin/categories?status=empty',
      entries: emptyCategories.map((category) => createGatewayEntry(
        `${category.groupName} · ${category.name}`,
        `/admin/categories?group=${encodeURIComponent(category.groupName)}`,
      )),
    })
  }

  const pendingReviewGroups = detail.pendingNominationGroups ?? []
  if (pendingReviewGroups.length > 0) {
    warnings.push({
      label: 'Offene Nominierungsgruppen',
      detail: `${pendingReviewGroups.length} Review-Gruppen sollten vor dem Voting entschieden werden.`,
      status: 'warn',
      to: '/admin/nominations?review=1',
      entries: pendingReviewGroups
        .slice(0, 6)
        .map((group) => createGatewayEntry(
          `${group.categoryGroupName} · ${group.displayName} · ${group.uniqueSubmitterCount} Viewer`,
          buildReviewGatewayLink(group.id, group.categoryGroupName),
        )),
    })
  }

  const contactedCandidates = detail.candidates.filter((candidate) => candidate.acceptanceStatus === 'contacted')
  const openCandidates = detail.candidates.filter((candidate) => candidate.acceptanceStatus === 'open')
  if (contactedCandidates.length + openCandidates.length > 0) {
    warnings.push({
      label: 'Kandidaten ohne finale Teilnahmezusage',
      detail: `${contactedCandidates.length} angefragt, ${openCandidates.length} offen.`,
      status: 'warn',
      to: '/admin/candidates?readiness=needs_decision',
      entries: [...contactedCandidates, ...openCandidates]
        .slice(0, 6)
        .map((candidate) => createGatewayEntry(
          `${candidate.displayName} · ${candidate.acceptanceStatus === 'contacted' ? 'angefragt' : 'offen'}`,
          `/admin/candidates?readiness=needs_decision&categoryId=${candidate.categoryId}&q=${encodeURIComponent(candidate.displayName)}`,
        )),
    })
  }

  const pendingClips = detail.clipSubmissions.filter((clip) => clip.status === 'pending')
  if (pendingClips.length > 0) {
    warnings.push({
      label: 'Offene Clip-Prüfungen',
      detail: `${pendingClips.length} Clip-Einreichungen warten noch auf Entscheidung.`,
      status: 'warn',
      to: '/admin/clips?status=pending',
      entries: pendingClips
        .slice(0, 6)
        .map((clip) => createGatewayEntry(
          `${clip.creator} · ${clip.title}`,
          `/admin/clips?status=pending&categoryId=${clip.categoryId}&q=${encodeURIComponent(clip.creator || clip.title)}`,
        )),
    })
  }

  return {
    action: 'activate',
    phase: 'Community Voting',
    title: 'Check vor Community Voting',
    subtitle: 'Vor dem Voting prüfen wir Kandidatenbasis, offene Reviews und organisatorische Restpunkte.',
    blockers,
    warnings,
    confirmsLabel: 'Voting trotzdem aktivieren',
  }
}

function buildCompletionGateway(
  detail: AdminSeasonDetailResponse,
  form: AdminSeasonForm,
): AdminSeasonPhaseGateway {
  const blockers: AdminSeasonGatewayCheckItem[] = []
  const warnings: AdminSeasonGatewayCheckItem[] = []
  const categoryIdsWithResults = new Set(detail.results.map((result) => result.categoryId))
  const missingWinnerCategories = detail.categories.filter((category) => !categoryIdsWithResults.has(category.id))
  if (!hasShowDayPassed(form)) {
    blockers.push({
      label: 'Award Show liegt noch nicht hinter uns',
      detail: form.showDate
        ? `Geplantes Show-Datum: ${formatLocalDate(form.showDate)}`
        : 'Es fehlt ein gültiges Show-Datum.',
      status: 'block',
      to: '/admin/years',
    })
  }

  if (missingWinnerCategories.length > 0) {
    blockers.push({
      label: 'Kategorien ohne Gewinner',
      detail: missingWinnerCategories.map((category) => category.name).slice(0, 4).join(', ')
        + (missingWinnerCategories.length > 4 ? ` +${missingWinnerCategories.length - 4} weitere` : ''),
      status: 'block',
      to: '/admin/winners?status=open',
      entries: missingWinnerCategories.map((category) => createGatewayEntry(
        `${category.groupName} · ${category.name}`,
        `/admin/winners?status=open&categoryId=${category.id}`,
      )),
    })
  }

  const pendingReviewGroups = detail.pendingNominationGroups ?? []
  if (pendingReviewGroups.length > 0) {
    warnings.push({
      label: 'Noch offene Reviews',
      detail: `${pendingReviewGroups.length} Nominierungsgruppen sind noch nicht entschieden.`,
      status: 'warn',
      to: '/admin/nominations?review=1',
      entries: pendingReviewGroups
        .slice(0, 6)
        .map((group) => createGatewayEntry(
          `${group.categoryGroupName} · ${group.displayName} · ${group.uniqueSubmitterCount} Viewer`,
          buildReviewGatewayLink(group.id, group.categoryGroupName),
        )),
    })
  }

  const categoriesWithoutCandidates = detail.categories.filter((category) =>
    !detail.candidates.some((candidate) => candidate.categoryId === category.id),
  )
  if (categoriesWithoutCandidates.length > 0) {
    warnings.push({
      label: 'Historische Lücken in der Kandidatenbasis',
      detail: `${categoriesWithoutCandidates.length} Kategorien hatten gar keine Kandidaten.`,
      status: 'warn',
      to: '/admin/categories?status=empty',
      entries: categoriesWithoutCandidates.map((category) => createGatewayEntry(
        `${category.groupName} · ${category.name}`,
        `/admin/categories?group=${encodeURIComponent(category.groupName)}`,
      )),
    })
  }

  return {
    action: 'complete',
    phase: 'Abgeschlossen',
    title: 'Abschluss-Gateway',
    subtitle: 'Bevor das Jahr archiviert wird, prüfen wir Gewinner, Show-Zeitpunkt und offene Restarbeiten.',
    blockers,
    warnings,
    confirmsLabel: 'Jahr trotzdem abschließen',
  }
}

function formatAuditDate(value: string) {
  const date = new Date(value)
  if (Number.isNaN(date.getTime())) {
    return value
  }

  return date.toLocaleString('de-DE', {
    day: '2-digit',
    month: '2-digit',
    year: 'numeric',
    hour: '2-digit',
    minute: '2-digit',
  })
}

function createGatewayEntry(label: string, to: string): AdminSeasonGatewayEntryLink {
  return { label, to }
}

function buildReviewGatewayLink(nominationGroupId: number, categoryGroupName: string): string {
  return `/admin/nominations?review=1&nominationId=${nominationGroupId}&categoryGroup=${encodeURIComponent(categoryGroupName)}`
}

function formatLocalDate(value: string | undefined | null) {
  if (!value) {
    return 'offen'
  }

  const [year, month, day] = value.split('-').map(Number)
  if (!year || !month || !day) {
    return value
  }

  const date = new Date(year, month - 1, day)
  if (Number.isNaN(date.getTime())) {
    return value
  }

  return date.toLocaleDateString('de-DE', {
    day: '2-digit',
    month: '2-digit',
    year: 'numeric',
  })
}
