import type { ComputedRef, Ref } from 'vue'

import type { AuthSession } from '../../types/awards'
import type { AwardsStore } from '../../stores/awards'
import type { HomeClipSubmitContext, HomeDisplayCategory, HomeNominationSubmitContext, HomePreviewPhase, HomeSuccessKind } from './homeLandingTypes'

export function useHomeParticipationSubmitActions(params: {
  store: AwardsStore
  displayCategories: ComputedRef<HomeDisplayCategory[]>
  activeCat: Ref<number>
  previewPhase: Ref<HomePreviewPhase>
  votes: Ref<Record<string, number>>
  submitted: Ref<boolean>
  submitting: Ref<boolean>
  formError: Ref<string>
  successKind: Ref<null | HomeSuccessKind>
  clipCatIdx: Ref<number>
  clipDsgvo: Ref<boolean>
  ensureViewerSession: () => Promise<AuthSession>
  loadMyParticipation: () => Promise<void>
  fallbackCreatorName: ComputedRef<string>
}) {
  const {
    store,
    displayCategories,
    activeCat,
    previewPhase,
    votes,
    submitted,
    submitting,
    formError,
    successKind,
    clipCatIdx,
    clipDsgvo,
    ensureViewerSession,
    loadMyParticipation,
    fallbackCreatorName,
  } = params

  function setCat(index: number) {
    activeCat.value = index
  }

  function pickNominee(categoryId: string, index: number) {
    votes.value = { ...votes.value, [categoryId]: index }
  }

  async function submitVote() {
    formError.value = ''
    if (submitting.value) return
    if (previewPhase.value !== 'voting') {
      formError.value = 'Das Voting ist in dieser Phase nicht verfügbar.'
      return
    }

    if (Object.keys(votes.value).length === 0) {
      formError.value = 'Bitte wähle mindestens eine Kategorie aus.'
      return
    }

    submitting.value = true
    try {
      const session = await ensureViewerSession()
      const entries = Object.entries(votes.value)
        .map(([categoryId, candidateIndex]) => {
          const category = displayCategories.value.find((item) => item.id === categoryId)
          const candidate = category?.candidates[candidateIndex]
          return category && candidate
            ? { categoryId: Number(category.id), candidateId: candidate.id }
            : null
        })
        .filter((entry: { categoryId: number; candidateId: number } | null): entry is { categoryId: number; candidateId: number } => entry !== null)

      if (entries.length === 0) {
        formError.value = 'Bitte wähle mindestens eine gültige Kategorie aus.'
        return
      }

      await store.submitVote({
        seasonId: store.overview.seasonId,
        twitchUserId: session.twitchUserId,
        entries,
      })
      await loadMyParticipation()
      submitted.value = true
      successKind.value = 'vote'
    } catch (error) {
      formError.value = error instanceof Error ? error.message : 'Deine Stimme konnte nicht gespeichert werden.'
    } finally {
      submitting.value = false
    }
  }

  function submitReminder() {
    submitted.value = true
    successKind.value = 'show'
  }

  function clipDsgvoChange() {
    clipDsgvo.value = !clipDsgvo.value
  }

  function onClipCatChange(event: Event) {
    const target = event.target as HTMLSelectElement
    clipCatIdx.value = Number.parseInt(target.value || '0', 10) || 0
  }

  async function submitNomination(nominationContext: HomeNominationSubmitContext) {
    formError.value = ''
    if (submitting.value) return
    if (previewPhase.value !== 'nomination') {
      formError.value = 'Nominierungen sind nur während der Nominierungsphase möglich.'
      return
    }

    const streamUrl = nominationContext.streamUrl.trim()
    if (!streamUrl) {
      formError.value = 'Bitte füge einen Stream- oder Kanal-Link hinzu.'
      return
    }

    if (!isHttpUrl(streamUrl)) {
      formError.value = 'Bitte gib einen gültigen http(s)-Link ein.'
      return
    }

    const category = displayCategories.value[nominationContext.categoryIndex]
    if (!category) {
      formError.value = 'Bitte wähle eine gültige Kategorie aus.'
      return
    }

    submitting.value = true
    try {
      const session = await ensureViewerSession()
      await store.submitNomination({
        year: store.overview.year,
        categoryId: Number(category.id),
        twitchUserId: session.twitchUserId,
        nominations: [{ streamUrl }],
      })
      await loadMyParticipation()
      submitted.value = true
      successKind.value = 'nomination'
    } catch (error) {
      formError.value = error instanceof Error ? error.message : 'Deine Nominierung konnte nicht gespeichert werden.'
    } finally {
      submitting.value = false
    }
  }

  async function submitClip(clipContext: HomeClipSubmitContext) {
    formError.value = ''
    if (!clipDsgvo.value || submitting.value) return
    if (previewPhase.value !== 'nomination') {
      formError.value = 'Clip-Einreichungen sind nur während der Nominierungsphase möglich.'
      return
    }

    const url = clipContext.clipUrl.trim()
    if (!url) {
      formError.value = 'Bitte gib einen Clip-Link ein.'
      return
    }

    if (!isHttpUrl(url)) {
      formError.value = 'Bitte gib einen gültigen http(s)-Link ein.'
      return
    }

    submitting.value = true
    try {
      const session = await ensureViewerSession()
      const category = displayCategories.value[clipCatIdx.value]
      const selectedCreator = resolveSelectedCreator(category, clipContext.selectedNomineeQuery)
      if (category?.candidates.length && !selectedCreator) {
        formError.value = 'Bitte wähle einen VTuber aus den Vorschlägen aus.'
        return
      }

      await store.submitClip({
        year: store.overview.year,
        categoryId: category ? Number(category.id) : null,
        candidateId: selectedCreator?.id ?? null,
        twitchUserId: session.twitchUserId,
        clipUrl: url,
        title: clipContext.description || `Clip fuer ${selectedCreator?.displayName ?? 'die Award-Show'}`,
        creator: selectedCreator?.displayName ?? fallbackCreatorName.value,
      })
      submitted.value = true
      successKind.value = 'clip'
    } catch (error) {
      formError.value = error instanceof Error ? error.message : 'Der Clip konnte nicht gespeichert werden.'
    } finally {
      submitting.value = false
    }
  }

  return {
    setCat,
    pickNominee,
    submitVote,
    submitReminder,
    clipDsgvoChange,
    onClipCatChange,
    submitNomination,
    submitClip,
  }
}

function resolveSelectedCreator(category: HomeDisplayCategory | undefined, query: string) {
  const candidates = category?.candidates ?? []
  const normalizedQuery = normalizeSearchValue(query)
  if (!normalizedQuery) return null

  return candidates.find((candidate) => normalizeSearchValue(candidate.displayName) === normalizedQuery)
    ?? candidates.find((candidate) => normalizeSearchValue(candidate.channelSlug) === normalizedQuery)
    ?? candidates.find((candidate) => normalizeSearchValue(candidate.displayName).includes(normalizedQuery))
    ?? null
}

function normalizeSearchValue(value: string) {
  return value.trim().toLowerCase()
}

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