<script setup lang="ts">
import { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue'
import { useDevMode } from '../../composables/useDevMode'
import { useDevModeEnvironment, type DevModeEnvironmentKey } from '../../composables/useDevModeEnvironment'
import { useDevModeToggles } from '../../composables/useDevModeToggles'
import { useModalDragResize } from '../../composables/useModalDragResize'
import { useFdsI18n } from '../../plugin/useFdsI18n'
import FdsButtonIcon from '../Buttons/FdsButtonIcon/FdsButtonIcon.vue'
import FdsBreakpointIndicator from './FdsBreakpointIndicator.vue'
import FdsDevModeOverlay from './FdsDevModeOverlay.vue'
import FdsDevModeStorage from './FdsDevModeStorage.vue'
import type { DevModeAction, DevModeToggle } from './types'

const props = withDefaults(
  defineProps<{
    environment?: DevModeEnvironmentKey | null
    customActions?: DevModeAction[]
    toggles?: DevModeToggle[]
  }>(),
  {
    environment: null,
    customActions: () => [],
    toggles: () => [],
  },
)

/** Checkbox state per toggle key – controlled by the consumer. */
const toggleChecked = defineModel<Record<string, boolean>>('toggleChecked', { default: () => ({}) })

/** Selected radio value per group – controlled by the consumer. */
const toggleSelected = defineModel<Record<string, string>>('toggleSelected', { default: () => ({}) })

const emit = defineEmits<{
  toggle: [payload: { key: string; value: boolean }]
  select: [payload: { key: string; group: string; value: string }]
  action: [payload: { key: string }]
}>()

const { isDevMode, toggleDevMode } = useDevMode()
const { toggles: registeredToggles, toggleChecked: registeredToggleChecked, toggleSelected: registeredToggleSelected } =
  useDevModeToggles()
const { t } = useFdsI18n()

const hideEnvBanner = ref(false)
const isStorageModalOpen = ref(false)
const bannerRef = ref<HTMLElement | null>(null)
const bannerStorageKey = computed(() => 'FdsDevMode')

const {
  isCustomPositioned,
  style: bannerDragStyle,
  onDragStart,
  restore: restoreBannerPosition,
  cleanup: cleanupBannerDrag,
} = useModalDragResize({
  movable: computed(() => true),
  resizable: computed(() => false),
  elementRef: bannerRef,
  heading: bannerStorageKey,
})

onBeforeUnmount(() => {
  cleanupBannerDrag()
})

const { environmentKey, isDevEnv, environmentText, isBannerEnvironment, isLocalhost } = useDevModeEnvironment(
  () => props.environment,
)

const renderEnvBanner = computed(() => {
  if (hideEnvBanner.value || environmentKey.value === null) return false
  return isBannerEnvironment.value
})

watch(
  renderEnvBanner,
  async (visible) => {
    if (!visible) return
    await nextTick()
    restoreBannerPosition()
  },
  { immediate: true },
)

const envBannerClasses = computed(() => {
  if (isLocalhost.value && isDevMode.value) return 'bg-green-500 text-white'
  if (isLocalhost.value && !isDevMode.value) return 'bg-yellow-400 text-gray-900'
  if (isDevMode.value) return 'bg-green-500 text-white'
  if (isDevEnv.value) return 'bg-yellow-400 text-gray-900'
  if (!isDevEnv.value) return 'bg-red-600 text-white-solid'
  return ''
})

const envBannerPositionClasses = computed(() => {
  if (isCustomPositioned.value) return 'fixed cursor-move select-none'
  if (isLocalhost.value) return 'fixed bottom-0 my-6 right-18'
  return 'fixed bottom-0 my-6 right-8'
})

const envBannerFillColor = computed(() => {
  if (isDevMode.value) return 'fill-white'
  if (isDevEnv.value) return 'fill-gray-900'
  if (environmentKey.value === 'preprod') return 'fill-white'
  return ''
})

const handleEnvBanner = () => {
  hideEnvBanner.value = true
}

const openStorageModal = () => {
  isStorageModalOpen.value = true
}

const devModeButtonText = computed(() => (isDevMode.value ? t('FdsDevMode.toggle.on') : t('FdsDevMode.toggle.off')))

const propToggles = computed(() => (Array.isArray(props.toggles) ? props.toggles : []))

const allToggles = computed(() => {
  const propKeys = new Set(propToggles.value.map((toggle) => toggle.key))
  const registeredOnly = registeredToggles.value.filter((toggle) => !propKeys.has(toggle.key))
  return [...propToggles.value, ...registeredOnly]
})

const mergedToggleChecked = computed(() => ({
  ...registeredToggleChecked.value,
  ...toggleChecked.value,
}))

const mergedToggleSelected = computed(() => ({
  ...registeredToggleSelected.value,
  ...toggleSelected.value,
}))

const overlayToggles = computed(() =>
  allToggles.value.map((toggle) => {
    if (toggle.type === 'radio') {
      return {
        ...toggle,
        checked: mergedToggleSelected.value[toggle.group] === toggle.value,
      }
    }

    return {
      ...toggle,
      checked: mergedToggleChecked.value[toggle.key] ?? false,
    }
  }),
)

const handleToggle = (payload: { key: string; value: boolean }) => {
  const toggle = allToggles.value.find((entry) => entry.key === payload.key)
  if (toggle?.type !== 'radio' && toggle?.type !== 'button') {
    toggle?.onToggle?.(payload.value)
  }

  if (registeredToggles.value.some((entry) => entry.key === payload.key)) {
    registeredToggleChecked.value = {
      ...registeredToggleChecked.value,
      [payload.key]: payload.value,
    }
  }

  toggleChecked.value = {
    ...toggleChecked.value,
    [payload.key]: payload.value,
  }

  emit('toggle', payload)
}

const handleSelect = (payload: { key: string; group: string; value: string }) => {
  const toggle = allToggles.value.find((entry) => entry.key === payload.key)
  if (toggle?.type === 'radio') {
    toggle.onSelect?.(payload.value)
  }

  if (registeredToggles.value.some((entry) => entry.key === payload.key)) {
    registeredToggleSelected.value = {
      ...registeredToggleSelected.value,
      [payload.group]: payload.value,
    }
  }

  toggleSelected.value = {
    ...toggleSelected.value,
    [payload.group]: payload.value,
  }

  emit('select', payload)
}

const handleAction = (payload: { key: string }) => {
  const toggle = allToggles.value.find((entry) => entry.key === payload.key)
  if (toggle?.type === 'button') {
    toggle.onAction?.()
  }
  emit('action', payload)
}
</script>

<template>
  <div
    v-if="renderEnvBanner"
    ref="bannerRef"
    class="font-bold rounded-xl z-50"
    :class="[envBannerPositionClasses, envBannerClasses]"
    :style="bannerDragStyle"
    @mousedown="onDragStart"
  >
    <div class="relative px-2 py-1">
      <FdsDevModeOverlay
        v-if="isDevMode"
        :environment="environment"
        :toggles="overlayToggles"
        @toggle="handleToggle"
        @select="handleSelect"
        @action="handleAction"
      />

      <div class="flex flex-col sm:flex-row items-center gap-2 sm:gap-1">
        <div class="flex items-center gap-1">
          <FdsBreakpointIndicator :popover-class="envBannerClasses" />
          <span>{{ environmentText }}</span>
        </div>

        <div class="flex items-center gap-1">
          <button
            @click="toggleDevMode"
            class="py-1 px-2 rounded text-xs font-bold bg-white bg-opacity-20 text-black hover:bg-opacity-30 min-w-[120px] cursor-pointer"
          >
            {{ devModeButtonText }}
          </button>

          <FdsButtonIcon icon="settings" :size="24" :class="envBannerFillColor" @click="openStorageModal" />
          <FdsButtonIcon @click="handleEnvBanner" icon="cross" :size="24" :class="envBannerFillColor" />
        </div>
      </div>
    </div>
  </div>

  <FdsDevModeStorage v-model:open="isStorageModalOpen" :customActions="customActions" />
</template>
