<template>
  <UApp :toaster="toasterConfig">
    <Transition name="shell-mode" mode="out-in">
      <div v-if="simpleMode" key="simple" class="shell-stage flex flex-col">
        <UAlert
          v-if="showConnectionBanner"
          color="warning"
          variant="soft"
          orientation="horizontal"
          :title="connectionBannerTitle"
        >
          <template #leading>
            <UIcon
              :name="connectionBannerIcon"
              :class="[
                'size-4 shrink-0 text-warning',
                socket.connectionStatus === 'connecting' ? 'animate-spin' : '',
              ]"
            />
          </template>

          <template v-if="socket.connectionStatus === 'disconnected'" #actions>
            <UButton
              color="neutral"
              variant="link"
              size="sm"
              class="px-0"
              @click="() => socket.reconnect()"
            >
              Reconnect
            </UButton>
          </template>
        </UAlert>

        <Simple @show_settings="() => (show_settings = true)" />
      </div>

      <div v-else key="regular" class="shell-stage flex flex-col">
        <Shutdown v-if="app_shutdown" />

        <div id="main_container" class="shell-root flex flex-col" v-else>
          <UAlert
            v-if="newVersionIsAvailable"
            color="success"
            variant="soft"
            orientation="horizontal"
            title="A new WebUI version is installed."
          >
            <template #leading>
              <UIcon name="i-lucide-info" class="size-4 shrink-0 text-success" />
            </template>

            <template #actions>
              <UButton color="neutral" variant="link" size="sm" class="px-0" @click="reloadPage">
                Reload app
              </UButton>
            </template>
          </UAlert>

          <UDashboardGroup
            storage="local"
            storage-key="ytptube-shell"
            class="shell-dashboard flex-1"
            :ui="{ base: 'relative flex min-h-full overflow-visible' }"
          >
            <UDashboardSidebar
              side="left"
              collapsible
              resizable
              :default-size="15"
              :min-size="10"
              :max-size="20"
              :collapsed-size="4"
              :ui="dashboardSidebarUi"
            >
              <template #header="{ collapsed }">
                <div
                  class="flex w-full min-w-0 items-center gap-2"
                  :class="collapsed ? 'justify-center' : ''"
                >
                  <span
                    class="inline-flex shrink-0 items-center justify-center transition-all duration-200"
                    :class="
                      collapsed
                        ? 'size-10 rounded-xl bg-elevated/80 ring ring-default shadow-xs'
                        : ''
                    "
                  >
                    <UIcon
                      v-if="'connecting' === socket.connectionStatus"
                      name="i-lucide-loader-circle"
                      :class="[collapsed ? 'size-6' : 'size-4', 'animate-spin']"
                    />
                    <UIcon v-else name="i-lucide-house" :class="collapsed ? 'size-6' : 'size-4'" />
                  </span>

                  <div v-if="false === collapsed" class="min-w-0">
                    <UTooltip :text="connectionStatusLabel">
                      <p class="truncate text-sm font-semibold" :class="connectionStatusColor">
                        YTPTube
                      </p>
                    </UTooltip>
                    <p v-if="config?.app?.instance_title" class="truncate text-xs text-toned">
                      {{ config.app.instance_title }}
                    </p>
                  </div>
                </div>
              </template>

              <template #default="{ collapsed }">
                <div class="flex h-full flex-col gap-6 px-1 py-1">
                  <div v-for="section in sidebarSections" :key="section.id" class="space-y-2">
                    <p
                      v-if="false === collapsed && section.label"
                      class="px-2 text-[11px] font-semibold tracking-[0.22em] text-toned uppercase"
                    >
                      {{ section.label }}
                    </p>

                    <UNavigationMenu
                      orientation="vertical"
                      :collapsed="collapsed"
                      :items="section.items"
                      :tooltip="true"
                      :ui="navigationUi(collapsed)"
                    />
                  </div>
                </div>
              </template>

              <template #footer="{ collapsed }">
                <div v-if="false === collapsed" class="w-full"></div>
              </template>
            </UDashboardSidebar>

            <UDashboardPanel class="min-w-0 bg-transparent" :ui="dashboardPanelUi">
              <template #header>
                <UDashboardNavbar :toggle="false" :title="pageTitle" :ui="dashboardNavbarUi">
                  <template #left>
                    <div class="flex items-center gap-2">
                      <UDashboardSidebarToggle class="lg:hidden" />
                      <UButton
                        to="/"
                        color="neutral"
                        variant="ghost"
                        size="sm"
                        icon="i-lucide-house"
                        class="lg:hidden"
                      >
                        Home
                      </UButton>
                      <UDashboardSidebarCollapse class="hidden lg:inline-flex" />
                    </div>
                  </template>

                  <template #right>
                    <div class="flex items-center gap-1 sm:gap-2">
                      <NotifyDropdown />

                      <UButton
                        color="neutral"
                        variant="ghost"
                        size="sm"
                        icon="i-lucide-refresh-cw"
                        @click="reloadPage"
                      >
                        <span class="hidden xl:inline">Reload</span>
                      </UButton>

                      <UDashboardSearchButton class="shrink-0" />

                      <UColorModeButton
                        color="neutral"
                        variant="ghost"
                        size="sm"
                        aria-label="Toggle color mode"
                      />

                      <UButton
                        color="neutral"
                        variant="ghost"
                        size="sm"
                        icon="i-lucide-settings-2"
                        @click="show_settings = !show_settings"
                      >
                        <span class="hidden xl:inline">WebUI Settings</span>
                      </UButton>

                      <UButton
                        v-if="true === config.app.is_native"
                        color="error"
                        variant="ghost"
                        size="sm"
                        icon="i-lucide-power"
                        @click="shutdownApp"
                      >
                        <span class="hidden xl:inline">Shutdown</span>
                      </UButton>
                    </div>
                  </template>
                </UDashboardNavbar>
              </template>

              <template #body>
                <div class="relative flex min-h-full min-w-0 max-w-full flex-1 flex-col">
                  <NuxtLoadingIndicator />

                  <div
                    class="flex min-h-0 min-w-0 max-w-full flex-1 flex-col px-4 py-4 sm:px-5 sm:py-5 lg:px-6"
                  >
                    <div class="flex min-h-0 min-w-0 max-w-full flex-1 flex-col gap-4">
                      <UAlert
                        v-if="!config.is_loaded"
                        :color="config.is_loading ? 'info' : 'error'"
                        variant="soft"
                        :title="
                          config.is_loading
                            ? 'Loading configuration...'
                            : 'Failed to load configuration'
                        "
                      >
                        <template #leading>
                          <UIcon
                            :name="
                              config.is_loading
                                ? 'i-lucide-loader-circle'
                                : 'i-lucide-triangle-alert'
                            "
                            :class="[
                              'size-4 shrink-0',
                              config.is_loading ? 'animate-spin text-info' : 'text-error',
                            ]"
                          />
                        </template>

                        <template #description>
                          <div class="space-y-3">
                            <p v-if="config.is_loading" class="text-sm text-default">
                              This usually takes less than a few seconds. If this is taking too
                              long,
                              <button
                                type="button"
                                class="font-semibold text-highlighted underline-offset-2 hover:underline"
                                @click="reloadPage"
                              >
                                click here
                              </button>
                              to reload the page.
                            </p>

                            <p v-else class="text-sm text-default">
                              Failed to load the application configuration. This likely indicates a
                              problem with the backend. Try to
                              <button
                                type="button"
                                class="font-semibold text-highlighted underline-offset-2 hover:underline"
                                @click="() => config.loadConfig(true)"
                              >
                                reload configuration
                              </button>
                              or
                              <button
                                type="button"
                                class="font-semibold text-highlighted underline-offset-2 hover:underline"
                                @click="reloadPage"
                              >
                                reload the page
                              </button>
                              .
                            </p>

                            <div
                              v-if="socket.error"
                              class="flex flex-wrap items-center gap-2 border-t border-default pt-3 text-error"
                            >
                              <UIcon name="i-lucide-triangle-alert" class="size-4" />
                              <span
                                class="inline-flex min-w-6 items-center justify-center rounded-full bg-error px-2 py-0.5 text-xs font-semibold text-white"
                              >
                                {{ socket.error_count }}
                              </span>
                              <span>
                                {{ socket.error }}. Check the developer console for more
                                information.
                              </span>
                            </div>
                          </div>
                        </template>
                      </UAlert>

                      <UAlert
                        v-if="showConnectionBanner"
                        color="warning"
                        variant="soft"
                        orientation="horizontal"
                        :title="connectionBannerTitle"
                      >
                        <template #leading>
                          <UIcon
                            :name="connectionBannerIcon"
                            :class="[
                              'size-4 shrink-0 text-warning',
                              socket.connectionStatus === 'connecting' ? 'animate-spin' : '',
                            ]"
                          />
                        </template>

                        <template v-if="socket.connectionStatus === 'disconnected'" #actions>
                          <UButton
                            color="neutral"
                            variant="link"
                            size="sm"
                            class="px-0"
                            @click="() => socket.reconnect()"
                          >
                            Reconnect
                          </UButton>
                        </template>
                      </UAlert>

                      <div
                        v-if="config.is_loaded"
                        class="flex min-h-0 min-w-0 max-w-full flex-1 flex-col"
                      >
                        <NuxtPage :isLoading="loadingImage" @reload_bg="() => loadImage(true)" />
                      </div>
                    </div>

                    <footer
                      v-if="config.is_loaded"
                      class="shell-footer mt-auto border-t border-default pt-6 text-sm text-toned"
                    >
                      <div class="grid gap-6 lg:grid-cols-[minmax(0,1.6fr)_minmax(0,1fr)]">
                        <div class="space-y-3">
                          <div class="flex flex-wrap items-center gap-x-3 gap-y-2">
                            <NuxtLink
                              href="https://github.com/ArabCoders/ytptube"
                              target="_blank"
                              class="inline-flex items-center gap-2 font-semibold text-highlighted"
                            >
                              <UIcon name="i-lucide-github" class="size-4" />
                              <span>YTPTube</span>
                            </NuxtLink>

                            <UTooltip :text="buildTooltip">
                              <span class="text-xs has-tooltip">
                                {{ config?.app?.app_version || 'unknown' }}
                              </span>
                            </UTooltip>
                          </div>

                          <p
                            v-if="config.app?.new_version"
                            class="flex flex-wrap items-center gap-2 text-xs"
                          >
                            <UIcon name="i-lucide-info" class="size-4 text-warning" />
                            <span>Update available:</span>
                            <NuxtLink
                              :href="`https://github.com/ArabCoders/ytptube/releases/tag/${config.app.new_version}`"
                              target="_blank"
                              class="font-semibold text-highlighted"
                            >
                              {{ config.app.new_version }}
                            </NuxtLink>
                          </p>

                          <button
                            v-else
                            type="button"
                            class="inline-flex items-center gap-2 text-xs text-left transition-colors hover:text-highlighted disabled:opacity-60"
                            :disabled="checkingUpdates"
                            @click="checkForUpdates"
                          >
                            <UIcon
                              :name="
                                checkingUpdates
                                  ? 'i-lucide-loader-circle'
                                  : 'i-lucide-circle-check-big'
                              "
                              :class="['size-4', checkingUpdates ? 'animate-spin' : '']"
                            />
                            <span>{{ updateCheckMessage }}</span>
                          </button>

                          <p v-if="config.app?.started" class="flex items-center gap-2 text-xs">
                            <UIcon name="i-lucide-clock-3" class="size-4" />
                            <UTooltip
                              :text="
                                'Started: ' +
                                moment.unix(config.app?.started).format('YYYY-MM-DD HH:mm Z')
                              "
                            >
                              <span class="has-tooltip">
                                {{ moment.unix(config.app?.started).fromNow() }}
                              </span>
                            </UTooltip>
                          </p>
                        </div>

                        <div
                          class="space-y-3 border-t border-default pt-4 lg:border-t-0 lg:border-l lg:pl-6 lg:pt-0"
                        >
                          <p class="text-xs font-semibold uppercase tracking-[0.2em] text-toned">
                            Powered by
                          </p>

                          <div class="space-y-2">
                            <div class="flex flex-wrap items-center gap-2">
                              <NuxtLink
                                href="https://github.com/yt-dlp/yt-dlp"
                                target="_blank"
                                class="inline-flex items-center gap-2 font-semibold text-highlighted"
                              >
                                <UIcon name="i-lucide-github" class="size-4" />
                                <span>yt-dlp</span>
                              </NuxtLink>

                              <span class="text-xs">{{
                                config?.app?.ytdlp_version || 'unknown'
                              }}</span>
                            </div>

                            <p
                              v-if="config.app?.yt_new_version"
                              class="flex flex-wrap items-center gap-2 text-xs"
                            >
                              <UTooltip text="Restart container to update yt-dlp">
                                <span class="has-tooltip">
                                  <UIcon name="i-lucide-info" class="size-4 text-warning" />
                                </span>
                              </UTooltip>
                              <span>Update available:</span>
                              <NuxtLink
                                :href="`https://github.com/yt-dlp/yt-dlp/releases/tag/${config.app.yt_new_version}`"
                                target="_blank"
                                class="font-semibold text-highlighted"
                              >
                                {{ config.app.yt_new_version }}
                              </NuxtLink>
                            </p>
                          </div>
                        </div>
                      </div>
                    </footer>
                  </div>

                  <ClientOnly>
                    <Dialog />
                  </ClientOnly>
                </div>
              </template>
            </UDashboardPanel>

            <UDashboardSearch
              v-model:open="showRouteSearch"
              :groups="routeSearchGroups"
              shortcut="meta_k"
              placeholder="Search routes and actions"
              :ui="{ modal: 'sm:max-w-3xl h-full sm:h-[28rem]' }"
            />
          </UDashboardGroup>
        </div>
      </div>
    </Transition>

    <SettingsPanel
      :isOpen="show_settings"
      :isLoading="loadingImage"
      @close="closeSettings()"
      @reload_bg="() => loadImage(true)"
      direction="right"
    />
  </UApp>
</template>

<script setup lang="ts">
import type { NavigationMenuItem } from '@nuxt/ui';
import { ref, onMounted, readonly } from 'vue';
import { useStorage } from '@vueuse/core';
import moment from 'moment';
import type { toastPosition } from '~/composables/useNotification';
import { getDocsNavigationEntries } from '~/composables/useDocs';
import type { YTDLPOption } from '~/types/ytdlp';
import { useDialog } from '~/composables/useDialog';
import Dialog from '~/components/Dialog.vue';
import Simple from '~/components/Simple.vue';
import Shutdown from '~/components/shutdown.vue';
import type { version_check } from '~/types';

type NavEntry = {
  id: string;
  label: string;
  description?: string;
  icon: string;
  to?: string;
  children?: NavEntry[];
};

type SidebarSection = {
  id: string;
  label?: string;
  items: Array<Array<NavigationMenuItem>>;
};

const socket = useSocketStore();
const config = useConfigStore();
const route = useRoute();
const loadedImage = ref();
const loadingImage = ref(false);
const bg_enable = useStorage('random_bg', true);
const bg_opacity = useStorage('random_bg_opacity', 0.95);
const app_shutdown = ref<boolean>(false);
const simpleMode = useStorage<boolean>('simple_mode', config.app.simple_mode || false);
const show_settings = ref(false);
const checkingUpdates = ref(false);
const updateCheckMessage = ref('Up to date - Click to check');
const showRouteSearch = ref(false);
const { alertDialog, confirmDialog } = useDialog();

const dashboardSidebarUi = {
  root: 'border-r border-default bg-default/95 backdrop-blur-sm',
  header: 'border-b border-default px-2.5 py-3',
  body: 'gap-3 px-2.5 py-3',
  footer: 'border-t border-default px-2.5 py-3',
};

const dashboardNavbarUi = {
  root: 'border-b border-default bg-transparent px-4 py-3 sm:px-5 lg:px-6',
  title: 'text-sm font-semibold text-highlighted',
  right: 'flex items-center shrink-0 gap-1.5',
};

const dashboardPanelUi = {
  root: 'min-w-0 min-h-screen max-w-full flex flex-1 flex-col bg-transparent',
  body: 'flex min-h-0 min-w-0 max-w-full flex-1 flex-col overflow-y-visible p-0',
};

const navigationUi = (collapsed: boolean) => ({
  root: 'w-full',
  list: 'gap-1.5',
  link: collapsed
    ? 'justify-center rounded-lg px-2 py-2'
    : 'rounded-lg px-2.5 py-2 text-sm font-medium text-default transition-colors',
  linkLeadingIcon: collapsed ? 'size-5' : 'size-4',
  linkLabel: collapsed ? 'hidden' : 'truncate',
});

const makeNavigationItem = (item: NavEntry): NavigationMenuItem => ({
  label: item.label,
  icon: item.icon,
  to: item.to,
  value: item.id,
});

const docsNavigationEntries = getDocsNavigationEntries();

const allNavItems = computed<NavEntry[]>(() => [
  {
    id: 'downloads',
    label: 'Downloads',
    description: 'Queued and completed downloads list.',
    icon: 'i-lucide-download',
    to: '/',
  },
  {
    id: 'files',
    label: 'Files',
    description: 'Browse downloaded files.',
    icon: 'i-lucide-folder-tree',
    to: '/browser',
  },
  {
    id: 'presets',
    label: 'Presets',
    description:
      'Presets are pre-defined command options for yt-dlp that you want to apply to given download.',
    icon: 'i-lucide-sliders-horizontal',
    to: '/presets',
  },
  {
    id: 'custom-fields',
    label: 'Custom Fields',
    description: 'Custom fields allow you to add new fields to the download form.',
    icon: 'i-lucide-braces',
    to: '/dl_fields',
  },
  {
    id: 'conditions',
    label: 'Conditions',
    description: 'Run yt-dlp custom match filter on returned info and apply options.',
    icon: 'i-lucide-filter',
    to: '/conditions',
  },
  {
    id: 'notifications',
    label: 'Notifications',
    description: 'Send notifications to your webhooks based on specified events or presets.',
    icon: 'i-lucide-bell',
    to: '/notifications',
  },
  {
    id: 'tasks',
    label: 'Tasks',
    icon: 'i-lucide-list-todo',
    children: [
      {
        id: 'tasks-list',
        label: 'Tasks',
        description: 'Queue playlist/channels for automatic download at specified intervals.',
        icon: 'i-lucide-list-todo',
        to: '/tasks',
      },
      {
        id: 'task-definitions',
        label: 'Task Definitions',
        description: 'Create definitions to turn any website into a downloadable feed of links.',
        icon: 'i-lucide-workflow',
        to: '/task_definitions',
      },
    ],
  },
  {
    id: 'tools',
    label: 'Tools',
    icon: 'i-lucide-wrench',
    children: [
      ...(config.app?.file_logging
        ? [
            {
              id: 'logs',
              label: 'Logs',
              description: 'Scroll near the top to load older logs.',
              icon: 'i-lucide-file-text',
              to: '/logs',
            },
          ]
        : []),
      ...(config.app.console_enabled
        ? [
            {
              id: 'console',
              label: 'Console',
              description: 'Run yt-dlp commands directly in a non-interactive session.',
              icon: 'i-lucide-terminal',
              to: '/console',
            },
          ]
        : []),
    ],
  },
  {
    id: 'docs',
    label: 'Docs',
    icon: 'i-lucide-book-open',
    children: [
      ...docsNavigationEntries,
      {
        id: 'changelog',
        label: 'Changelog',
        description:
          'Latest project changes, loaded remotely when available and falling back to the bundled changelog file.',
        icon: 'i-lucide-list',
        to: '/changelog',
      },
    ],
  },
]);

const pageTitle = computed(() => {
  const path = route.path;
  const flat = allNavItems.value.flatMap((item) => [item, ...(item.children || [])]);
  const match = flat
    .filter((item) => item.to && (item.to === '/' ? path === '/' : path.startsWith(item.to)))
    .sort((left, right) => (right.to?.length || 0) - (left.to?.length || 0))[0];
  return match?.label || 'YTPTube';
});

const buildTooltip = computed(
  () =>
    `Build: ${config.app?.app_build_date}, Branch: ${config.app?.app_branch}, SHA: ${config.app?.app_commit_sha}`,
);

const showConnectionBanner = computed(() => socket.connectionStatus !== 'connected');

const connectionBannerTitle = computed(() => {
  switch (socket.connectionStatus) {
    case 'connecting':
      return 'Connecting to websocket server...';
    case 'disconnected':
    default:
      return 'Websocket connection lost.';
  }
});

const connectionBannerIcon = computed(() =>
  socket.connectionStatus === 'connecting' ? 'i-lucide-loader-circle' : 'i-lucide-info',
);

const sidebarSections = computed<Array<SidebarSection>>(() => {
  const topLevelItems = (ids: string[]) =>
    ids
      .map((id) => allNavItems.value.find((item) => item.id === id))
      .filter((item): item is NavEntry => Boolean(item))
      .map((item) => makeNavigationItem(item));

  const childItems = (id: string) =>
    (allNavItems.value.find((item) => item.id === id)?.children || []).map((item) =>
      makeNavigationItem(item),
    );

  return [
    {
      id: 'downloads',
      label: 'Downloads',
      items:
        topLevelItems(['downloads', 'files']).length > 0
          ? [topLevelItems(['downloads', 'files'])]
          : [],
    },
    {
      id: 'automation',
      label: 'Automation',
      items: childItems('tasks').length > 0 ? [childItems('tasks')] : [],
    },
    {
      id: 'configuration',
      label: 'Configuration',
      items: [
        topLevelItems(['presets', 'custom-fields']),
        topLevelItems(['conditions', 'notifications']),
      ].filter((group) => group.length > 0),
    },
    {
      id: 'tools',
      label: 'Tools',
      items: childItems('tools').length > 0 ? [childItems('tools')] : [],
    },
    {
      id: 'docs',
      label: 'Docs',
      items: childItems('docs').length > 0 ? [childItems('docs')] : [],
    },
  ].filter((section) => section.items.some((group) => group.length > 0));
});

const routeSearchGroups = computed(() => [
  {
    id: 'routes',
    label: 'Routes',
    items: allNavItems.value.flatMap((item) => {
      const self = item.to
        ? [
            {
              label: item.label,
              description: item.description,
              icon: item.icon,
              suffix: item.to,
              onSelect: () => handleRouteSelect(item),
            },
          ]
        : [];

      const children = (item.children || []).map((child) => ({
        label: child.label,
        description: child.description,
        icon: child.icon,
        suffix: child.to || '',
        onSelect: () => handleRouteSelect(child),
      }));

      return [...self, ...children];
    }),
  },
  {
    id: 'downloads',
    label: 'Downloads',
    items: [
      config.paused
        ? {
            label: 'Resume Downloads',
            description: 'Resume globally paused downloads.',
            icon: 'i-lucide-play',
            onSelect: () => void resumeDownloads(),
          }
        : {
            label: 'Pause Downloads',
            description: 'Globally pause all non-active downloads.',
            icon: 'i-lucide-pause',
            onSelect: () => void pauseDownloads(),
          },
    ],
  },
  {
    id: 'preferences',
    label: 'Preferences',
    items: [
      {
        label: 'WebUI Settings',
        description: 'Adjust interface behavior and download defaults.',
        icon: 'i-lucide-settings-2',
        onSelect: () => void openSettings(),
      },
    ],
  },
]);

const closeRouteSearch = async (): Promise<void> => {
  if (!showRouteSearch.value) {
    return;
  }

  showRouteSearch.value = false;
  await nextTick();
};

const pauseDownloads = async (): Promise<void> => {
  await closeRouteSearch();
  await request('/api/system/pause', { method: 'POST' });
};

const resumeDownloads = async (): Promise<void> => {
  await closeRouteSearch();
  await request('/api/system/resume', { method: 'POST' });
};

const openSettings = async (): Promise<void> => {
  await closeRouteSearch();
  show_settings.value = true;
};

const syncShellModeClass = () => {
  const html = document.documentElement;
  html.classList.toggle('simple-mode', simpleMode.value);
};

const handleRouteSelect = async (item: NavEntry) => {
  await closeRouteSearch();

  if (item.to) {
    await navigateTo(item.to);
  }
};

const checkForUpdates = async () => {
  if (checkingUpdates.value) {
    return;
  }

  const msg = 'Up to date - Click to check';

  try {
    checkingUpdates.value = true;
    updateCheckMessage.value = 'Checking...';

    const response = await fetch('/api/system/check-updates', { method: 'POST' });

    if (!response.ok) {
      await response.json();
      updateCheckMessage.value = 'Check failed';
      setTimeout(() => (updateCheckMessage.value = msg), 3000);
      return;
    }

    const data = await parse_api_response<version_check>(await response.json());

    if ('update_available' === data.app.status) {
      config.app.new_version = data.app.new_version;
    }
    if (data.ytdlp && 'update_available' === data.ytdlp.status) {
      config.app.yt_new_version = data.ytdlp.new_version;
    }

    if ('update_available' === data.app.status) {
      updateCheckMessage.value = 'Update found!';
    } else if ('up_to_date' === data.app.status && 'up_to_date' === data.ytdlp?.status) {
      updateCheckMessage.value = 'Up to date ✓';
      setTimeout(() => (updateCheckMessage.value = msg), 3000);
    } else if ('up_to_date' === data.app.status && 'update_available' === data.ytdlp?.status) {
      updateCheckMessage.value = 'Up to date ✓';
      setTimeout(() => (updateCheckMessage.value = msg), 3000);
    } else {
      updateCheckMessage.value = 'Check failed';
      setTimeout(() => (updateCheckMessage.value = msg), 3000);
    }
  } catch (e) {
    console.error('Update check failed:', e);
    updateCheckMessage.value = 'Check failed';
    setTimeout(() => (updateCheckMessage.value = msg), 3000);
  } finally {
    checkingUpdates.value = false;
  }
};

onMounted(async () => {
  syncShellModeClass();

  try {
    await config.loadConfig();
  } catch {}

  try {
    const opts = await request('/api/yt-dlp/options');
    if (opts.ok) {
      config.ytdlp_options = (await opts.json()) as Array<YTDLPOption>;
    }
  } catch {}

  try {
    socket.connect();
  } catch {}

  try {
    await handleImage(bg_enable.value);
  } catch {}
});

watch(bg_enable, async (v) => await handleImage(v));
watch(simpleMode, () => syncShellModeClass());
watch(bg_opacity, (v) => {
  if (false === bg_enable.value) {
    return;
  }
  document.querySelector('body')?.setAttribute('style', `opacity: ${v}`);
});

watch(loadedImage, () => {
  if (false === bg_enable.value) {
    return;
  }

  const html = document.documentElement;
  const body = document.querySelector('body');

  const style = {
    'background-color': 'unset',
    display: 'block',
    'min-height': '100%',
    'min-width': '100%',
    'background-image': `url(${loadedImage.value})`,
  } as any;

  html.setAttribute(
    'style',
    Object.keys(style)
      .map((k) => `${k}: ${style[k]}`)
      .join('; ')
      .trim(),
  );
  html.classList.add('bg-fanart');
  body?.setAttribute('style', `opacity: ${bg_opacity.value}`);
});

const handleImage = async (enabled: boolean) => {
  if (false === enabled) {
    const html = document.documentElement;
    const body = document.querySelector('body');

    html.classList.remove('bg-fanart');

    if (html.getAttribute('style')) {
      html.removeAttribute('style');
    }

    if (body?.getAttribute('style')) {
      body.removeAttribute('style');
    }

    loadedImage.value = '';
    return;
  }

  if (loadedImage.value) {
    return;
  }

  await loadImage();
};

const loadImage = async (force = false) => {
  if (loadingImage.value) {
    return;
  }

  try {
    loadingImage.value = true;

    let url = '/api/random/background';
    if (force) {
      url += '?force=true';
    }

    const imgRequest = await request(url);
    if (200 !== imgRequest.status) {
      return;
    }

    loadedImage.value = URL.createObjectURL(await imgRequest.blob());
  } catch (e) {
    console.error(e);
  } finally {
    loadingImage.value = false;
  }
};

const useVersionUpdate = () => {
  const newVersionIsAvailable = ref(false);
  const nuxtApp = useNuxtApp();
  nuxtApp.hooks.addHooks({
    'app:manifest:update': () => {
      newVersionIsAvailable.value = true;
    },
  });

  return {
    newVersionIsAvailable: readonly(newVersionIsAvailable),
  };
};

const { newVersionIsAvailable } = useVersionUpdate();

const closeSettings = () => (show_settings.value = false);

const shutdownApp = async () => {
  if (false === config.app.is_native) {
    await alertDialog({
      title: 'Shutdown Unavailable',
      message: 'The shutdown feature is only available when running as native application.',
    });
    return;
  }

  const { status } = await confirmDialog({
    title: 'Shutdown Application',
    message: 'Are you sure you want to shutdown the application?',
  });

  if (false === status) {
    return;
  }

  try {
    const resp = await fetch('/api/system/shutdown', { method: 'POST' });
    if (!resp.ok) {
      const body = await resp.json();
      await alertDialog({
        title: 'Shutdown Failed',
        message: `Failed to shutdown the application: ${body.error || resp.statusText || resp.status}`,
      });
      return;
    }
    app_shutdown.value = true;
    await nextTick();
  } catch (e: any) {
    await alertDialog({
      title: 'Shutdown Failed',
      message: `Failed to shutdown the application: ${e.message || e}`,
    });
  }
};

const reloadPage = () => window.location.reload();

const connectionStatusColor = computed(() => {
  switch (socket.connectionStatus) {
    case 'connected':
      return 'text-success';
    case 'connecting':
      return 'text-warning';
    case 'disconnected':
    default:
      return 'text-error';
  }
});

const connectionStatusLabel = computed(() => {
  switch (socket.connectionStatus) {
    case 'connected':
      return 'Connected';
    case 'connecting':
      return 'Connecting...';
    case 'disconnected':
    default:
      return 'Disconnected';
  }
});

const toastPosition = useStorage<toastPosition>('toast_position', 'top-right');

const toasterConfig = computed(() => ({
  position: toastPosition.value,
  max: 5,
  expand: true,
  progress: true,
}));
</script>

<style>
.shell-stage {
  min-height: 100vh;
  min-height: 100dvh;
}

.shell-mode-enter-active,
.shell-mode-leave-active {
  transition:
    opacity 0.28s ease,
    transform 0.34s cubic-bezier(0.22, 1, 0.36, 1);
  will-change: opacity, transform;
}

.shell-mode-enter-from,
.shell-mode-leave-to {
  opacity: 0;
  transform: translateY(18px) scale(0.985);
}

.shell-root {
  min-height: 100vh;
  min-height: 100dvh;
}

.shell-dashboard {
  min-height: 100vh;
  min-height: 100dvh;
}

.shell-footer {
  padding-top: 1.5rem;
  padding-bottom: 1.5rem;
}
</style>
