import { nextTick, onBeforeUnmount, onMounted, ref, watch, type ComponentPublicInstance, type Ref } from 'vue'
import type { Router } from 'vue-router'

import { useAuthStore } from '../../stores/auth'
import { useAwardsStore } from '../../stores/awards'

type AuthStore = ReturnType<typeof useAuthStore>
type AwardsStore = ReturnType<typeof useAwardsStore>
type HomeTimelineKey = 'nomination' | 'voting' | 'preparation' | 'show' | 'completed'

interface PhaseCountdownTarget {
  label: string
  target: number
  direction?: 'until' | 'since'
}

interface TimelineScheduleItem {
  key: HomeTimelineKey
  title: string
  startMs: number
  endMs: number
}

interface UseHomeLandingViewEffectsParams {
  router: Router
  store: AwardsStore
  authStore: AuthStore
  modalOpen: Readonly<Ref<boolean>>
  accountModalOpen: Readonly<Ref<boolean>>
  privacyModalOpen: Readonly<Ref<boolean>>
  archiveModalOpen: Readonly<Ref<boolean>>
  submitted: Readonly<Ref<boolean>>
  streamLive: Readonly<Ref<boolean>>
  archiveYear: Readonly<Ref<number>>
  nominationPhase: Readonly<Ref<boolean>>
  votingPhase: Readonly<Ref<boolean>>
  preparationPhase: Readonly<Ref<boolean>>
  completedPhase: Readonly<Ref<boolean>>
  initializeHomeInteractions: () => Promise<void>
  submitClip: (clipContext: { clipUrl: string; selectedNomineeQuery: string; description: string }) => Promise<void>
}

export function useHomeLandingViewEffects(params: UseHomeLandingViewEffectsParams) {
  const {
    router,
    store,
    authStore,
    modalOpen,
    accountModalOpen,
    privacyModalOpen,
    archiveModalOpen,
    submitted,
    streamLive,
    archiveYear,
    nominationPhase,
    votingPhase,
    preparationPhase,
    completedPhase,
    initializeHomeInteractions,
    submitClip,
  } = params

  const rootEl = ref<HTMLElement | null>(null)
  const landingLoaderVisible = ref(true)
  const stickyCountdownVisible = ref(false)
  const clipUrlEl = ref<HTMLInputElement | null>(null)
  const clipNomSearchEl = ref<HTMLInputElement | null>(null)
  const clipDescEl = ref<HTMLTextAreaElement | null>(null)
  const countdownRefs = {
    labelEl: ref<HTMLElement | null>(null),
    streamLabelEl: ref<HTMLElement | null>(null),
    dEl: ref<HTMLElement | null>(null),
    hEl: ref<HTMLElement | null>(null),
    mEl: ref<HTMLElement | null>(null),
    sEl: ref<HTMLElement | null>(null),
    bdEl: ref<HTMLElement | null>(null),
    bhEl: ref<HTMLElement | null>(null),
    bmEl: ref<HTMLElement | null>(null),
    bsEl: ref<HTMLElement | null>(null),
    stickyLabelEl: ref<HTMLElement | null>(null),
    sbdEl: ref<HTMLElement | null>(null),
    sbhEl: ref<HTMLElement | null>(null),
    sbmEl: ref<HTMLElement | null>(null),
    sbsEl: ref<HTMLElement | null>(null),
  }
  let timer: ReturnType<typeof setInterval> | null = null
  let landingLoaderTimer: ReturnType<typeof setTimeout> | null = null

  function syncScrollLock() {
    const shouldLock = landingLoaderVisible.value
      || modalOpen.value
      || accountModalOpen.value
      || privacyModalOpen.value
      || archiveModalOpen.value

    document.documentElement.style.overflow = shouldLock ? 'hidden' : ''
    document.body.style.overflow = shouldLock ? 'hidden' : ''
  }

  function setRootEl(element: Element | ComponentPublicInstance | null) {
    rootEl.value = element instanceof HTMLElement ? element : null
  }

  function handleClipSubmit() {
    return submitClip({
      clipUrl: clipUrlEl.value?.value.trim() ?? '',
      selectedNomineeQuery: clipNomSearchEl.value?.value.trim() ?? '',
      description: clipDescEl.value?.value.trim() ?? '',
    })
  }

  function assignDomRefs() {
    const root = rootEl.value
    if (!root) return
    countdownRefs.labelEl.value = root.querySelector('[data-dc-ref="phaseCountdownLabelRef"]')
    countdownRefs.streamLabelEl.value = root.querySelector('[data-dc-ref="streamCountdownLabelRef"]')
    countdownRefs.dEl.value = root.querySelector('[data-dc-ref="dRef"]')
    countdownRefs.hEl.value = root.querySelector('[data-dc-ref="hRef"]')
    countdownRefs.mEl.value = root.querySelector('[data-dc-ref="mRef"]')
    countdownRefs.sEl.value = root.querySelector('[data-dc-ref="sRef"]')
    countdownRefs.bdEl.value = root.querySelector('[data-dc-ref="bdRef"]')
    countdownRefs.bhEl.value = root.querySelector('[data-dc-ref="bhRef"]')
    countdownRefs.bmEl.value = root.querySelector('[data-dc-ref="bmRef"]')
    countdownRefs.bsEl.value = root.querySelector('[data-dc-ref="bsRef"]')
    countdownRefs.stickyLabelEl.value = root.querySelector('[data-dc-ref="stickyCountdownLabelRef"]')
    countdownRefs.sbdEl.value = root.querySelector('[data-dc-ref="sbdRef"]')
    countdownRefs.sbhEl.value = root.querySelector('[data-dc-ref="sbhRef"]')
    countdownRefs.sbmEl.value = root.querySelector('[data-dc-ref="sbmRef"]')
    countdownRefs.sbsEl.value = root.querySelector('[data-dc-ref="sbsRef"]')
    clipUrlEl.value = root.querySelector('[data-dc-ref="clipUrlRef"]')
    clipNomSearchEl.value = root.querySelector('[data-dc-ref="clipNomSearchRef"]')
    clipDescEl.value = root.querySelector('[data-dc-ref="clipDescRef"]')
  }

  function wireInteractiveStyles() {
    const root = rootEl.value
    if (!root) return
    root.querySelectorAll<HTMLElement>('[style-hover]').forEach((element) => {
      if (element.dataset.hoverBound === '1') return
      element.dataset.hoverBound = '1'
      const hover = element.getAttribute('style-hover') ?? ''
      element.addEventListener('mouseenter', () => {
        const base = element.getAttribute('style') ?? ''
        element.dataset.hoverBaseStyle = base
        element.setAttribute('style', `${base}${hover}`)
      })
      element.addEventListener('mouseleave', () => {
        element.setAttribute('style', element.dataset.hoverBaseStyle ?? '')
        delete element.dataset.hoverBaseStyle
      })
    })
    root.querySelectorAll<HTMLElement>('[style-focus]').forEach((element) => {
      if (element.dataset.focusBound === '1') return
      element.dataset.focusBound = '1'
      const focus = element.getAttribute('style-focus') ?? ''
      element.addEventListener('focus', () => {
        const base = element.getAttribute('style') ?? ''
        element.dataset.focusBaseStyle = base
        element.setAttribute('style', `${base}${focus}`)
      })
      element.addEventListener('blur', () => {
        element.setAttribute('style', element.dataset.focusBaseStyle ?? '')
        delete element.dataset.focusBaseStyle
      })
    })
  }

  function setupDom() {
    assignDomRefs()
    wireInteractiveStyles()
  }

  function tick() {
    const now = Date.now()
    const activeTarget = resolvePhaseCountdownTarget(store, resolveSelectedPhaseKey(), now)
    const showTarget = parseShowStartMs(store)
    const activeTargetMs = Number.isNaN(activeTarget.target) ? showTarget : activeTarget.target
    const activeCountdown = split(activeTarget.direction === 'since' ? now - activeTargetMs : activeTargetMs - now)
    const showCompleted = resolveIsCompletedPhase(store.overview.currentPhase)
    const showStarted = !Number.isNaN(showTarget) && now >= showTarget
    const showCountdown = split(showCompleted ? 0 : showStarted ? now - showTarget : showTarget - now)

    setText(countdownRefs.labelEl.value, activeTarget.label)
    setText(countdownRefs.streamLabelEl.value, showCompleted ? 'Award abgeschlossen' : showStarted ? 'Live seit' : 'Finale in')
    setText(countdownRefs.dEl.value, pad(activeCountdown.d))
    setText(countdownRefs.hEl.value, pad(activeCountdown.h))
    setText(countdownRefs.mEl.value, pad(activeCountdown.m))
    setText(countdownRefs.sEl.value, pad(activeCountdown.s))
    setText(countdownRefs.bdEl.value, pad(showCountdown.d))
    setText(countdownRefs.bhEl.value, pad(showCountdown.h))
    setText(countdownRefs.bmEl.value, pad(showCountdown.m))
    setText(countdownRefs.bsEl.value, pad(showCountdown.s))
    setText(countdownRefs.stickyLabelEl.value, showCompleted ? 'Award abgeschlossen' : showStarted ? 'Stream läuft seit' : 'Finale startet in')
    setText(countdownRefs.sbdEl.value, pad(showCountdown.d))
    setText(countdownRefs.sbhEl.value, pad(showCountdown.h))
    setText(countdownRefs.sbmEl.value, pad(showCountdown.m))
    setText(countdownRefs.sbsEl.value, pad(showCountdown.s))
  }

  function updateStickyCountdownVisibility() {
    if (modalOpen.value || accountModalOpen.value || privacyModalOpen.value || archiveModalOpen.value) {
      stickyCountdownVisible.value = false
      return
    }

    const hero = rootEl.value?.querySelector('.home-hero')
    if (!(hero instanceof HTMLElement)) {
      stickyCountdownVisible.value = false
      return
    }

    stickyCountdownVisible.value = hero.getBoundingClientRect().bottom <= 0
  }

  function resolveSelectedPhaseKey(): HomeTimelineKey {
    if (nominationPhase.value) return 'nomination'
    if (votingPhase.value) return 'voting'
    if (preparationPhase.value) return 'preparation'
    if (completedPhase.value) return 'completed'
    return 'show'
  }

  watch([landingLoaderVisible, modalOpen, accountModalOpen, privacyModalOpen, archiveModalOpen], () => {
    syncScrollLock()
    nextTick(setupDom)
    updateStickyCountdownVisibility()
  })

  watch([submitted, streamLive, archiveYear], () => {
    nextTick(setupDom)
  })

  onMounted(async () => {
    syncScrollLock()
    if (!authStore.hydrated) {
      await authStore.hydrate()
    }
    await store.loadHomeData()
    if (store.lastPublicErrorKind) {
      await router.replace({
        name: 'network-error',
        query: { from: '/' },
      })
      return
    }
    await initializeHomeInteractions()
    setupDom()
    tick()
    updateStickyCountdownVisibility()
    window.addEventListener('scroll', updateStickyCountdownVisibility, { passive: true })
    window.addEventListener('resize', updateStickyCountdownVisibility)
    timer = setInterval(tick, 1000)
    landingLoaderTimer = setTimeout(() => {
      landingLoaderVisible.value = false
    }, 1500)
  })

  onBeforeUnmount(() => {
    document.documentElement.style.overflow = ''
    document.body.style.overflow = ''
    window.removeEventListener('scroll', updateStickyCountdownVisibility)
    window.removeEventListener('resize', updateStickyCountdownVisibility)
    if (timer) clearInterval(timer)
    if (landingLoaderTimer) clearTimeout(landingLoaderTimer)
  })

  return {
    setRootEl,
    landingLoaderVisible,
    stickyCountdownVisible,
    handleClipSubmit,
  }
}

function pad(value: number) {
  return String(value).padStart(2, '0')
}

function parseBackendDateMs(value: string, endOfDay = false) {
  if (!value) return Number.NaN
  const date = new Date(`${value}T${endOfDay ? '23:59:59' : '00:00:00'}`)
  return date.getTime()
}

function parseShowStartMs(store: AwardsStore) {
  const date = store.overview.showDate
  if (!date) return Number.NaN
  const time = normalizeBackendTime(store.overview.showStartsAt)
  return new Date(`${date}T${time}`).getTime()
}

function normalizeBackendTime(value: string) {
  if (!value) return '20:00:00'
  return value.length === 5 ? `${value}:00` : value
}

function resolvePhaseCountdownTarget(
  store: AwardsStore,
  selectedPhaseKey: HomeTimelineKey,
  now: number,
): PhaseCountdownTarget {
  if (selectedPhaseKey === 'completed' || resolveIsCompletedPhase(store.overview.currentPhase)) {
    return {
      label: 'Award-Jahr abgeschlossen',
      target: now,
    }
  }

  const schedule = buildTimelineSchedule(store)
  const selectedIndex = schedule.findIndex((item) => item.key === selectedPhaseKey)
  const selectedItem = selectedIndex >= 0 ? schedule[selectedIndex] : null

  if (selectedItem) {
    const selectedTarget = resolveItemCountdownTarget(selectedItem, now)
    if (selectedTarget) {
      return selectedTarget
    }

    const nextTarget = schedule
      .slice(selectedIndex + 1)
      .map((item) => resolveItemCountdownTarget(item, now))
      .find((target): target is PhaseCountdownTarget => Boolean(target))

    if (nextTarget) {
      return nextTarget
    }
  }

  const globalTarget = schedule
    .map((item) => resolveItemCountdownTarget(item, now))
    .find((target): target is PhaseCountdownTarget => Boolean(target))

  if (globalTarget) {
    return globalTarget
  }

  const showTarget = parseBackendDateMs(store.overview.showDate, true)
  return {
    label: 'Phase abgeschlossen',
    target: Number.isNaN(showTarget) ? now : showTarget,
  }
}

function buildTimelineSchedule(store: AwardsStore): TimelineScheduleItem[] {
  const phaseOrder: Exclude<HomeTimelineKey, 'completed'>[] = ['nomination', 'voting', 'preparation', 'show']

  return phaseOrder
    .map((key): TimelineScheduleItem | null => {
      const entry = store.overview.timeline.find((item) => item.key === key)
      const fallbackDate = key === 'show' ? store.overview.showDate : ''
      const startsAt = entry?.startsAt || fallbackDate
      const endsAt = entry?.endsAt || fallbackDate
      const startMs = key === 'show' ? parseShowStartMs(store) : parseBackendDateMs(startsAt)
      const endMs = parseBackendDateMs(endsAt, true)

      if (Number.isNaN(startMs) || Number.isNaN(endMs)) {
        return null
      }

      return {
        key,
        title: entry?.title || phaseTitle(key),
        startMs,
        endMs,
      }
    })
    .filter((item): item is TimelineScheduleItem => item !== null)
}

function resolveItemCountdownTarget(item: TimelineScheduleItem, now: number): PhaseCountdownTarget | null {
  if (now < item.startMs) {
    return {
      label: `${item.title} startet in`,
      target: item.startMs,
    }
  }

  if (now <= item.endMs) {
    if (item.key === 'show') {
      return {
        label: 'Award-Show läuft seit',
        target: item.startMs,
        direction: 'since',
      }
    }

    return {
      label: `${item.title} noch offen`,
      target: item.endMs,
    }
  }

  return null
}

function resolveIsCompletedPhase(currentPhase: string) {
  const value = currentPhase.trim().toLowerCase()
  return value.includes('abgeschlossen') || value.includes('archiv') || value.includes('complete') || value.includes('ended')
}

function phaseTitle(key: HomeTimelineKey) {
  return key === 'nomination'
    ? 'Nominierung'
    : key === 'voting'
      ? 'Voting'
      : key === 'preparation'
        ? 'Aufbereitung'
        : 'Award Show'
}

function split(milliseconds: number) {
  const seconds = Math.floor(Math.max(0, milliseconds) / 1000)
  return {
    d: Math.floor(seconds / 86400),
    h: Math.floor((seconds % 86400) / 3600),
    m: Math.floor((seconds % 3600) / 60),
    s: seconds % 60,
  }
}

function setText(element: HTMLElement | null, value: string) {
  if (element) {
    element.textContent = value
  }
}
