<template>
  <div
    data-testid="settings-dialog"
    :class="
      teamWorkspacesEnabled
        ? 'flex h-full w-full overflow-auto flex-col md:flex-row'
        : 'settings-container'
    "
  >
    <ScrollPanel
      :class="
        teamWorkspacesEnabled
          ? 'w-full md:w-64 md:min-w-64 md:max-w-64 shrink-0 p-2'
          : 'settings-sidebar w-48 shrink-0 p-2 2xl:w-64'
      "
    >
      <div :class="teamWorkspacesEnabled ? 'px-4' : ''">
        <SearchBox
          v-model:model-value="searchQuery"
          class="settings-search-box mb-2 w-full"
          :placeholder="
            $t('g.searchPlaceholder', { subject: $t('g.settings') })
          "
          :debounce-time="128"
          autofocus
          @search="handleSearch"
        />
      </div>
      <Listbox
        v-model="activeCategory"
        :options="groupedMenuTreeNodes"
        option-label="translatedLabel"
        option-group-label="label"
        option-group-children="children"
        scroll-height="100%"
        :option-disabled="
          (option: SettingTreeNode) =>
            !queryIsEmpty && !searchResultsCategories.has(option.label ?? '')
        "
        :class="
          teamWorkspacesEnabled
            ? 'w-full border-none bg-transparent'
            : 'w-full border-none'
        "
      >
        <!-- Workspace mode: custom group headers -->
        <template v-if="teamWorkspacesEnabled" #optiongroup="{ option }">
          <h3 class="text-xs font-semibold uppercase text-muted m-0 pt-6 pb-2">
            {{ option.translatedLabel ?? option.label }}
          </h3>
        </template>
        <!-- Legacy mode: divider between groups -->
        <template v-else #optiongroup>
          <Divider class="my-0" />
        </template>
        <!-- Custom option template with data-testid for stable test selectors -->
        <template #option="{ option }">
          <span
            :data-testid="`settings-tab-${option.key}`"
            class="settings-tab-option"
          >
            <WorkspaceSidebarItem
              v-if="teamWorkspacesEnabled && option.key === 'workspace'"
            />
            <template v-else>{{ option.translatedLabel }}</template>
          </span>
        </template>
      </Listbox>
    </ScrollPanel>
    <Divider layout="vertical" class="mx-1 hidden md:flex 2xl:mx-4" />
    <Divider layout="horizontal" class="flex md:hidden" />
    <Tabs
      :value="tabValue"
      :lazy="true"
      :class="
        teamWorkspacesEnabled
          ? 'h-full flex-1 overflow-auto scrollbar-custom'
          : 'settings-content h-full w-full'
      "
    >
      <TabPanels class="settings-tab-panels h-full w-full pr-0">
        <PanelTemplate value="Search Results">
          <SettingsPanel :setting-groups="searchResults" />
        </PanelTemplate>

        <PanelTemplate
          v-for="category in settingCategories"
          :key="category.key"
          :value="category.label ?? ''"
        >
          <template #header>
            <CurrentUserMessage v-if="tabValue === 'Comfy'" />
            <ColorPaletteMessage v-if="tabValue === 'Appearance'" />
          </template>
          <SettingsPanel :setting-groups="sortedGroups(category)" />
        </PanelTemplate>

        <Suspense v-for="panel in panels" :key="panel.node.key">
          <component :is="panel.component" v-bind="panel.props" />
          <template #fallback>
            <div>{{ $t('g.loadingPanel', { panel: panel.node.label }) }}</div>
          </template>
        </Suspense>
      </TabPanels>
    </Tabs>
  </div>
</template>

<script setup lang="ts">
import Divider from 'primevue/divider'
import Listbox from 'primevue/listbox'
import ScrollPanel from 'primevue/scrollpanel'
import TabPanels from 'primevue/tabpanels'
import Tabs from 'primevue/tabs'
import { computed, nextTick, onBeforeUnmount, watch } from 'vue'

import SearchBox from '@/components/common/SearchBox.vue'
import CurrentUserMessage from '@/components/dialog/content/setting/CurrentUserMessage.vue'
import PanelTemplate from '@/components/dialog/content/setting/PanelTemplate.vue'
import WorkspaceSidebarItem from '@/components/dialog/content/setting/WorkspaceSidebarItem.vue'
import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'
import { useFeatureFlags } from '@/composables/useFeatureFlags'
import { isCloud } from '@/platform/distribution/types'
import ColorPaletteMessage from '@/platform/settings/components/ColorPaletteMessage.vue'
import SettingsPanel from '@/platform/settings/components/SettingsPanel.vue'
import { useSettingSearch } from '@/platform/settings/composables/useSettingSearch'
import { useSettingUI } from '@/platform/settings/composables/useSettingUI'
import type { SettingTreeNode } from '@/platform/settings/settingStore'
import type { ISettingGroup, SettingParams } from '@/platform/settings/types'
import { flattenTree } from '@/utils/treeUtil'

const { defaultPanel, scrollToSettingId } = defineProps<{
  defaultPanel?:
    | 'about'
    | 'keybinding'
    | 'extension'
    | 'server-config'
    | 'user'
    | 'credits'
    | 'subscription'
    | 'workspace'
    | 'secrets'
  scrollToSettingId?: string
}>()

const { flags } = useFeatureFlags()
const teamWorkspacesEnabled = computed(
  () => isCloud && flags.teamWorkspacesEnabled
)

const {
  activeCategory,
  defaultCategory,
  settingCategories,
  groupedMenuTreeNodes,
  panels
} = useSettingUI(defaultPanel, scrollToSettingId)

const {
  searchQuery,
  searchResultsCategories,
  queryIsEmpty,
  inSearch,
  handleSearch: handleSearchBase,
  getSearchResults
} = useSettingSearch()

const authActions = useFirebaseAuthActions()

// Get max sortOrder from settings in a group
const getGroupSortOrder = (group: SettingTreeNode): number =>
  Math.max(0, ...flattenTree<SettingParams>(group).map((s) => s.sortOrder ?? 0))

// Sort groups for a category
const sortedGroups = (category: SettingTreeNode): ISettingGroup[] => {
  return [...(category.children ?? [])]
    .sort((a, b) => {
      const orderDiff = getGroupSortOrder(b) - getGroupSortOrder(a)
      return orderDiff !== 0 ? orderDiff : a.label.localeCompare(b.label)
    })
    .map((group) => ({
      label: group.label,
      settings: flattenTree<SettingParams>(group).sort((a, b) => {
        const sortOrderA = a.sortOrder ?? 0
        const sortOrderB = b.sortOrder ?? 0

        return sortOrderB - sortOrderA
      })
    }))
}

const handleSearch = (query: string) => {
  handleSearchBase(query.trim())
  activeCategory.value = query ? null : defaultCategory.value
}

// Get search results
const searchResults = computed<ISettingGroup[]>(() =>
  getSearchResults(activeCategory.value)
)

const tabValue = computed<string>(() =>
  inSearch.value ? 'Search Results' : (activeCategory.value?.label ?? '')
)

// Scroll to and highlight the target setting once the correct tab renders.
if (scrollToSettingId) {
  const stopScrollWatch = watch(
    tabValue,
    () => {
      void nextTick(() => {
        const el = document.querySelector(
          `[data-setting-id="${CSS.escape(scrollToSettingId)}"]`
        )
        if (!el) return
        stopScrollWatch()
        el.scrollIntoView({ behavior: 'smooth', block: 'center' })
        el.classList.add('setting-highlight')
        el.addEventListener(
          'animationend',
          () => el.classList.remove('setting-highlight'),
          { once: true }
        )
      })
    },
    { immediate: true }
  )
  onBeforeUnmount(stopScrollWatch)
}

// Don't allow null category to be set outside of search.
// In search mode, the active category can be null to show all search results.
watch(activeCategory, (_, oldValue) => {
  if (!tabValue.value) {
    activeCategory.value = oldValue
  }
  if (activeCategory.value?.key === 'credits') {
    void authActions.fetchBalance()
  }
})
</script>

<style>
.settings-tab-panels {
  padding-top: 0 !important;
}

@media (prefers-reduced-motion: no-preference) {
  .setting-highlight {
    animation: setting-highlight-pulse 1.5s ease-in-out;
  }
}

@keyframes setting-highlight-pulse {
  0%,
  100% {
    background-color: transparent;
  }
  30% {
    background-color: color-mix(
      in srgb,
      var(--p-primary-color) 15%,
      transparent
    );
  }
}
</style>

<style scoped>
/* Legacy mode styles (when teamWorkspacesEnabled is false) */
.settings-container {
  display: flex;
  height: 70vh;
  width: 60vw;
  max-width: 64rem;
  overflow: hidden;
}

.settings-content {
  overflow-x: auto;
}

@media (max-width: 768px) {
  .settings-container {
    flex-direction: column;
    height: auto;
    width: 80vw;
  }

  .settings-sidebar {
    width: 100%;
  }

  .settings-content {
    height: 350px;
  }
}

/* Hide the first group separator in legacy mode */
.settings-sidebar :deep(.p-listbox-option-group:nth-child(1)) {
  display: none;
}
</style>
