import { computed, reactive, ref, watch } from 'vue'
import { useRoute } from 'vue-router'

import { api } from '../../lib/api'
import { SOCIAL_ICON_OPTIONS, socialIconOptionForValue } from '../../lib/socialIcons'
import { useAwardsStore } from '../../stores/awards'
import type { AdminCandidateDeletePreview, AdminCandidateItem, AdminWorkflowRule } from '../../types/awards'

interface CandidateForm {
  categoryId: number
  displayName: string
  channelSlug: string
  platform: string
  acceptanceStatus: string
  acceptanceNote: string
  clipCompilationUrl: string
  clipCompilationTitle: string
  clipCompilationPlatform: string
  clipEmbedStatus: string
}

type CandidateRuleNotice = {
  mode: 'warn' | 'block'
  message: string
}

const pageSize = 10
const maxFinalistsRuleKey = 'max_finalists_per_category'
const maxCandidateAppearancesRuleKey = 'max_candidate_appearances'
const acceptanceStatusOptions = [
  { label: 'Offen', value: 'open', description: 'Noch nicht kontaktiert' },
  { label: 'Angefragt', value: 'contacted', description: 'Kontakt läuft' },
  { label: 'Angenommen', value: 'accepted', description: 'nimmt teil' },
  { label: 'Abgesagt', value: 'declined', description: 'nicht voting-bereit' },
]
const clipEmbedStatusOptions = [
  { label: 'Noch nicht geprüft', value: 'unchecked' },
  { label: 'Einbettbar', value: 'embeddable' },
  { label: 'Nur Link', value: 'link_only' },
  { label: 'Nicht nutzbar', value: 'blocked' },
]
const readinessFilterOptions = [
  { label: 'Alle', value: 'all' },
  { label: 'Zusage offen', value: 'needs_decision' },
  { label: 'Offen', value: 'open' },
  { label: 'Angefragt', value: 'contacted' },
  { label: 'Angenommen', value: 'accepted' },
  { label: 'Abgesagt', value: 'declined' },
  { label: 'Clip fehlt', value: 'missing_clip' },
  { label: 'Embed prüfen', value: 'embed_review' },
]

export function useAdminCandidateManager() {
  const store = useAwardsStore()
  const route = useRoute()
  const saving = ref(false)
  const deleting = ref(false)
  const adminMessage = ref('')
  const adminError = ref('')
  const search = ref('')
  const categoryFilter = ref<number | null>(null)
  const readinessFilter = ref('all')
  const page = ref(1)
  const modalOpen = ref(false)
  const editingId = ref<number | 'new' | null>(null)
  const openedRouteCandidateId = ref<number | null>(null)
  const candidateToDelete = ref<AdminCandidateItem | null>(null)
  const deletePreviewLoading = ref(false)
  const candidateDeletePreview = ref<AdminCandidateDeletePreview | null>(null)
  const form = reactive<CandidateForm>({
    categoryId: 0,
    displayName: '',
    channelSlug: '',
    platform: 'Twitch',
    acceptanceStatus: 'open',
    acceptanceNote: '',
    clipCompilationUrl: '',
    clipCompilationTitle: '',
    clipCompilationPlatform: '',
    clipEmbedStatus: 'unchecked',
  })

  const seasonDetail = computed(() => store.adminSeasonDetail)
  const workflowRules = computed(() => store.adminWorkflowRules.rules)
  const selectedSeasonId = computed(() => store.adminSelectedSeasonId)
  const categoryOptions = computed(() =>
    seasonDetail.value.categories.map((category) => ({ label: `${category.groupName} · ${category.name}`, value: category.id })),
  )
  const preferredCreateCategoryId = computed(() => {
    if (categoryFilter.value) {
      return categoryFilter.value
    }

    return seasonDetail.value.categories.find((category) =>
      category.viewerRangeMin !== null || category.viewerRangeMax !== null,
    )?.id ?? seasonDetail.value.categories[0]?.id ?? 0
  })
  const categoryFilterOptions = computed(() => [{ label: 'Alle Kategorien', value: null }, ...categoryOptions.value])
  const categoryLabelMap = computed<Record<number, string>>(() =>
    Object.fromEntries(seasonDetail.value.categories.map((category) => [category.id, `${category.groupName} · ${category.name}`])),
  )
  const duplicateCandidateKeys = computed(() => {
    const counts = new Map<string, number>()
    for (const candidate of seasonDetail.value.candidates) {
      const nameKey = createCandidateDuplicateKey(candidate, 'name')
      const slugKey = createCandidateDuplicateKey(candidate, 'slug')
      counts.set(nameKey, (counts.get(nameKey) ?? 0) + 1)
      if (candidate.channelSlug.trim()) {
        counts.set(slugKey, (counts.get(slugKey) ?? 0) + 1)
      }
    }
    return counts
  })
  const duplicateCandidateCount = computed(() =>
    seasonDetail.value.candidates.filter((candidate) => hasDuplicateCandidateKey(candidate, duplicateCandidateKeys.value)).length,
  )
  const candidateWorkflowNotices = computed(() => buildCandidateWorkflowNotices(
    seasonDetail.value.candidates,
    workflowRules.value,
  ))
  const candidateIdentitySummaries = computed(() => buildCandidateIdentitySummaries(
    seasonDetail.value.candidates,
    seasonDetail.value.results,
  ))
  const acceptedCandidateCount = computed(() =>
    seasonDetail.value.candidates.filter((candidate) => candidate.acceptanceStatus === 'accepted').length,
  )
  const clipReadyCount = computed(() =>
    seasonDetail.value.candidates.filter((candidate) => hasUsableCompilation(candidate)).length,
  )
  const actionNeededCount = computed(() =>
    seasonDetail.value.candidates.filter((candidate) =>
      candidate.acceptanceStatus !== 'accepted'
      || !hasUsableCompilation(candidate)
      || candidate.clipEmbedStatus === 'unchecked',
    ).length,
  )
  const filteredCandidates = computed(() => {
    const query = search.value.trim().toLowerCase()
    let list = seasonDetail.value.candidates

    if (categoryFilter.value) {
      list = list.filter((candidate) => candidate.categoryId === categoryFilter.value)
    }

    if (readinessFilter.value !== 'all') {
      list = list.filter((candidate) => {
        if (readinessFilter.value === 'needs_decision') {
          return candidate.acceptanceStatus === 'open' || candidate.acceptanceStatus === 'contacted'
        }

        if (readinessFilter.value === 'missing_clip') {
          return !hasUsableCompilation(candidate)
        }

        if (readinessFilter.value === 'embed_review') {
          return Boolean(candidate.clipCompilationUrl?.trim()) && candidate.clipEmbedStatus === 'unchecked'
        }

        return candidate.acceptanceStatus === readinessFilter.value
      })
    }

    if (query) {
      list = list.filter((candidate) =>
        [candidate.displayName, candidate.channelSlug, candidate.platform, categoryLabelMap.value[candidate.categoryId] ?? '']
          .join(' ')
          .toLowerCase()
          .includes(query),
      )
    }

    return list
  })
  const totalPages = computed(() => Math.max(1, Math.ceil(filteredCandidates.value.length / pageSize)))
  const pagedCandidates = computed(() => {
    const start = (page.value - 1) * pageSize
    return filteredCandidates.value.slice(start, start + pageSize)
  })
  const rangeStart = computed(() => (filteredCandidates.value.length === 0 ? 0 : (page.value - 1) * pageSize + 1))
  const rangeEnd = computed(() => Math.min(page.value * pageSize, filteredCandidates.value.length))
  const modalTitle = computed(() => (editingId.value === 'new' ? 'Kandidat anlegen' : 'Kandidat bearbeiten'))
  const canSave = computed(() =>
    Boolean(
      selectedSeasonId.value
      && form.categoryId
      && form.displayName.trim()
      && form.channelSlug.trim()
      && form.platform.trim()
      && isValidOptionalUrl(form.clipCompilationUrl),
    ),
  )
  const candidatePlatformOptions = computed(() => SOCIAL_ICON_OPTIONS.filter((option) => option.key !== 'website'))
  const selectedPlatformValue = computed(() => socialIconOptionForValue(form.platform)?.key ?? 'custom')
  const editorIdentitySummary = computed(() => {
    const identityKey = editorIdentityKey(form, editingId.value, seasonDetail.value.candidates)
    if (!identityKey) {
      return null
    }

    return candidateIdentitySummaries.value[identityKey] ?? {
      appearances: 0,
      acceptedAppearances: 0,
      winnerPlacements: 0,
      nominationTally: 0,
    }
  })
  const editorRuleNotices = computed<CandidateRuleNotice[]>(() => {
    if (!selectedSeasonId.value || !form.categoryId) {
      return []
    }

    const notices: CandidateRuleNotice[] = []
    const finalistsRule = workflowRules.value.find((rule) => rule.key === maxFinalistsRuleKey)
    const appearancesRule = workflowRules.value.find((rule) => rule.key === maxCandidateAppearancesRuleKey)
    const activeCandidates = seasonDetail.value.candidates.filter((candidate) => candidate.acceptanceStatus !== 'declined')
    const currentCandidateId = typeof editingId.value === 'number' ? editingId.value : null

    if (finalistsRule?.enabled) {
      const categoryCount = activeCandidates.filter((candidate) =>
        candidate.categoryId === form.categoryId && candidate.id !== currentCandidateId,
      ).length + 1

      if (categoryCount > finalistsRule.limit) {
        notices.push({
          mode: finalistsRule.mode === 'warn' ? 'warn' : 'block',
          message: `${categoryCount}/${finalistsRule.limit} finale Kandidat:innen in dieser Kategorie.`,
        })
      }
    }

    if (appearancesRule?.enabled) {
      const identityKey = editorIdentityKey(form, editingId.value, seasonDetail.value.candidates)
      if (identityKey) {
        const appearanceCount = activeCandidates.filter((candidate) =>
          candidate.id !== currentCandidateId && candidateIdentityKey(candidate) === identityKey,
        ).length + 1

        if (appearanceCount > appearancesRule.limit) {
          notices.push({
            mode: appearancesRule.mode === 'warn' ? 'warn' : 'block',
            message: `${appearanceCount}/${appearancesRule.limit} Kandidaturen für diese Person.`,
          })
        }
      }
    }

    return notices
  })

  function applyRouteFilters() {
    const rawQuery = Array.isArray(route.query.q) ? route.query.q[0] : route.query.q
    const rawCategoryId = Array.isArray(route.query.categoryId) ? route.query.categoryId[0] : route.query.categoryId
    const rawReadiness = Array.isArray(route.query.readiness) ? route.query.readiness[0] : route.query.readiness
    const rawCandidateId = Array.isArray(route.query.candidateId) ? route.query.candidateId[0] : route.query.candidateId

    search.value = typeof rawQuery === 'string' ? rawQuery : ''

    const parsedCategoryId = Number(rawCategoryId)
    categoryFilter.value = Number.isFinite(parsedCategoryId) && parsedCategoryId > 0 ? parsedCategoryId : null

    const nextReadiness = typeof rawReadiness === 'string' ? rawReadiness : 'all'
    readinessFilter.value = readinessFilterOptions.some((option) => option.value === nextReadiness) ? nextReadiness : 'all'

    const parsedCandidateId = Number(rawCandidateId)
    if (!Number.isFinite(parsedCandidateId) || parsedCandidateId <= 0) {
      openedRouteCandidateId.value = null
      return
    }

    if (openedRouteCandidateId.value === parsedCandidateId) {
      return
    }

    const candidate = seasonDetail.value.candidates.find((item) => item.id === parsedCandidateId)
    if (candidate) {
      openedRouteCandidateId.value = parsedCandidateId
      openEdit(candidate)
    }
  }

  watch([search, categoryFilter, readinessFilter, () => seasonDetail.value.candidates.length], () => {
    page.value = 1
  })

  watch(totalPages, (max) => {
    if (page.value > max) {
      page.value = max
    }
  })

  watch(
    () => [route.query.q, route.query.categoryId, route.query.readiness, route.query.candidateId, seasonDetail.value.candidates.length] as const,
    () => {
      applyRouteFilters()
    },
    { immediate: true },
  )

  function clearFilters() {
    search.value = ''
    categoryFilter.value = null
    readinessFilter.value = 'all'
  }

  function resetPreparationForm() {
    form.acceptanceStatus = 'open'
    form.acceptanceNote = ''
    form.clipCompilationUrl = ''
    form.clipCompilationTitle = ''
    form.clipCompilationPlatform = ''
    form.clipEmbedStatus = 'unchecked'
  }

  function openCreate() {
    adminMessage.value = ''
    adminError.value = ''
    editingId.value = 'new'
    form.categoryId = preferredCreateCategoryId.value
    form.displayName = ''
    form.channelSlug = ''
    form.platform = 'Twitch'
    resetPreparationForm()
    modalOpen.value = true
  }

  function openEdit(candidate: AdminCandidateItem) {
    adminMessage.value = ''
    adminError.value = ''
    editingId.value = candidate.id
    form.categoryId = candidate.categoryId
    form.displayName = candidate.displayName
    form.channelSlug = candidate.channelSlug
    form.platform = candidate.platform
    form.acceptanceStatus = candidate.acceptanceStatus || 'open'
    form.acceptanceNote = candidate.acceptanceNote ?? ''
    form.clipCompilationUrl = candidate.clipCompilationUrl ?? ''
    form.clipCompilationTitle = candidate.clipCompilationTitle ?? ''
    form.clipCompilationPlatform = candidate.clipCompilationPlatform ?? ''
    form.clipEmbedStatus = candidate.clipEmbedStatus || 'unchecked'
    modalOpen.value = true
  }

  function handlePlatformSelection(value: string) {
    if (value === 'custom') {
      if (socialIconOptionForValue(form.platform)) {
        form.platform = ''
      }
      return
    }

    const option = socialIconOptionForValue(value)
    form.platform = option?.label ?? value
  }

  async function saveModal() {
    if (!canSave.value || !selectedSeasonId.value) {
      return
    }

    saving.value = true
    adminError.value = ''

    try {
      if (editingId.value === 'new') {
        await store.createAdminCandidate(selectedSeasonId.value, buildCandidatePayload())
        adminMessage.value = `„${form.displayName}" wurde angelegt.`
      } else if (typeof editingId.value === 'number') {
        await store.updateAdminCandidate(editingId.value, selectedSeasonId.value, buildCandidatePayload())
        adminMessage.value = `„${form.displayName}" wurde gespeichert.`
      }
      modalOpen.value = false
    } catch (error) {
      adminError.value = error instanceof Error ? error.message : 'Speichern fehlgeschlagen.'
    } finally {
      saving.value = false
    }
  }

  function buildCandidatePayload() {
    const clipUrl = form.clipCompilationUrl.trim()
    return {
      categoryId: form.categoryId,
      displayName: form.displayName,
      channelSlug: form.channelSlug,
      platform: form.platform,
      acceptanceStatus: form.acceptanceStatus,
      acceptanceNote: form.acceptanceNote.trim() || null,
      clipCompilationUrl: clipUrl || null,
      clipCompilationTitle: clipUrl ? form.clipCompilationTitle.trim() || null : null,
      clipCompilationPlatform: clipUrl ? form.clipCompilationPlatform.trim() || null : null,
      clipEmbedStatus: clipUrl ? form.clipEmbedStatus : 'unchecked',
    }
  }

  async function confirmDelete() {
    if (!candidateToDelete.value || !selectedSeasonId.value) {
      return
    }

    deleting.value = true
    adminError.value = ''

    try {
      await store.deleteAdminCandidate(candidateToDelete.value.id, selectedSeasonId.value)
      adminMessage.value = `„${candidateToDelete.value.displayName}" wurde gelöscht.`
      closeDeleteModal()
    } catch (error) {
      adminError.value = error instanceof Error ? error.message : 'Löschen fehlgeschlagen.'
    } finally {
      deleting.value = false
    }
  }

  async function openDelete(candidate: AdminCandidateItem) {
    candidateToDelete.value = candidate
    candidateDeletePreview.value = null
    deletePreviewLoading.value = true
    adminError.value = ''

    try {
      candidateDeletePreview.value = await api.getAdminCandidateDeletePreview(candidate.id)
    } catch (error) {
      adminError.value = error instanceof Error ? error.message : 'Löschvorschau konnte nicht geladen werden.'
    } finally {
      deletePreviewLoading.value = false
    }
  }

  function closeDeleteModal() {
    candidateToDelete.value = null
    candidateDeletePreview.value = null
    deletePreviewLoading.value = false
  }

  return {
    seasonDetail,
    saving,
    deleting,
    adminMessage,
    adminError,
    search,
    categoryFilter,
    readinessFilter,
    page,
    categoryOptions,
    categoryFilterOptions,
    categoryLabelMap,
    duplicateCandidateKeys,
    candidateWorkflowNotices,
    candidateIdentitySummaries,
    duplicateCandidateCount,
    acceptedCandidateCount,
    clipReadyCount,
    actionNeededCount,
    filteredCandidates,
    pagedCandidates,
    totalPages,
    rangeStart,
    rangeEnd,
    modalOpen,
    form,
    modalTitle,
    canSave,
    candidatePlatformOptions,
    selectedPlatformValue,
    editorIdentitySummary,
    editorRuleNotices,
    acceptanceStatusOptions,
    clipEmbedStatusOptions,
    readinessFilterOptions,
    candidateToDelete,
    deletePreviewLoading,
    candidateDeletePreview,
    clearFilters,
    openCreate,
    openEdit,
    openDelete,
    closeDeleteModal,
    handlePlatformSelection,
    saveModal,
    confirmDelete,
  }
}

function buildCandidateWorkflowNotices(candidates: AdminCandidateItem[], rules: AdminWorkflowRule[]) {
  const notices: Record<number, CandidateRuleNotice[]> = {}
  const activeCandidates = candidates.filter((candidate) => candidate.acceptanceStatus !== 'declined')
  const finalistsRule = rules.find((rule) => rule.key === maxFinalistsRuleKey)
  const appearancesRule = rules.find((rule) => rule.key === maxCandidateAppearancesRuleKey)

  if (finalistsRule?.enabled) {
    const categoryCounts = new Map<number, number>()
    for (const candidate of activeCandidates) {
      categoryCounts.set(candidate.categoryId, (categoryCounts.get(candidate.categoryId) ?? 0) + 1)
    }

    for (const candidate of activeCandidates) {
      const count = categoryCounts.get(candidate.categoryId) ?? 0
      if (count > finalistsRule.limit) {
        addCandidateNotice(notices, candidate.id, finalistsRule, `${count}/${finalistsRule.limit} finale Kandidat:innen in dieser Kategorie.`)
      }
    }
  }

  if (appearancesRule?.enabled) {
    const identityCounts = new Map<string, number>()
    for (const candidate of activeCandidates) {
      const key = candidateIdentityKey(candidate)
      identityCounts.set(key, (identityCounts.get(key) ?? 0) + 1)
    }

    for (const candidate of activeCandidates) {
      const count = identityCounts.get(candidateIdentityKey(candidate)) ?? 0
      if (count > appearancesRule.limit) {
        addCandidateNotice(notices, candidate.id, appearancesRule, `${count}/${appearancesRule.limit} Kandidaturen für diese Person.`)
      }
    }
  }

  return notices
}

function addCandidateNotice(
  notices: Record<number, CandidateRuleNotice[]>,
  candidateId: number,
  rule: AdminWorkflowRule,
  message: string,
) {
  const mode = rule.mode === 'warn' ? 'warn' : 'block'
  notices[candidateId] = [...(notices[candidateId] ?? []), { mode, message }]
}

function candidateIdentityKey(candidate: Pick<AdminCandidateItem, 'displayName' | 'channelSlug'>) {
  if ('streamerIdentityId' in candidate && typeof candidate.streamerIdentityId === 'number') {
    return `identity:${candidate.streamerIdentityId}`
  }
  const channel = candidate.channelSlug.trim().replace(/^@+/, '').toLowerCase()
  return channel ? `slug:${channel}` : `name:${candidate.displayName.trim().toLowerCase()}`
}

function editorIdentityKey(
  form: Pick<CandidateForm, 'displayName' | 'channelSlug'>,
  editingId: number | 'new' | null,
  candidates: AdminCandidateItem[],
) {
  if (typeof editingId === 'number') {
    const existing = candidates.find((candidate) => candidate.id === editingId)
    if (existing?.streamerIdentityId) {
      return `identity:${existing.streamerIdentityId}`
    }
  }

  const channel = form.channelSlug.trim().replace(/^@+/, '').toLowerCase()
  if (channel) {
    return `slug:${channel}`
  }

  const name = form.displayName.trim().toLowerCase()
  return name ? `name:${name}` : null
}

function createCandidateDuplicateKey(candidate: AdminCandidateItem, field: 'name' | 'slug') {
  const value = field === 'name' ? candidate.displayName : candidate.channelSlug
  return `${candidate.categoryId}:${field}:${value.trim().toLowerCase()}`
}

function hasDuplicateCandidateKey(candidate: AdminCandidateItem, candidateKeys: Map<string, number>) {
  return (candidateKeys.get(createCandidateDuplicateKey(candidate, 'name')) ?? 0) > 1
    || (candidateKeys.get(createCandidateDuplicateKey(candidate, 'slug')) ?? 0) > 1
}

function hasUsableCompilation(candidate: AdminCandidateItem) {
  return Boolean(candidate.clipCompilationUrl?.trim()) && candidate.clipEmbedStatus !== 'blocked'
}

function buildCandidateIdentitySummaries(candidates: AdminCandidateItem[], results: Array<{ candidateId: number }>) {
  const winnerCandidateIds = new Set(results.map((result) => result.candidateId))
  const summaries: Record<string, {
    appearances: number
    acceptedAppearances: number
    winnerPlacements: number
    nominationTally: number
  }> = {}

  for (const candidate of candidates) {
    const key = candidateIdentityKey(candidate)
    const existing = summaries[key] ?? {
      appearances: 0,
      acceptedAppearances: 0,
      winnerPlacements: 0,
      nominationTally: 0,
    }

    existing.appearances += candidate.acceptanceStatus === 'declined' ? 0 : 1
    existing.acceptedAppearances += candidate.acceptanceStatus === 'accepted' ? 1 : 0
    existing.winnerPlacements += winnerCandidateIds.has(candidate.id) ? 1 : 0
    existing.nominationTally = Math.max(existing.nominationTally, candidate.nominationTally ?? 0)
    summaries[key] = existing
  }

  return summaries
}

function isValidOptionalUrl(value: string) {
  const trimmed = value.trim()
  if (!trimmed) return true

  try {
    const url = new URL(trimmed)
    return url.protocol === 'http:' || url.protocol === 'https:'
  } catch {
    return false
  }
}
