<script setup lang="ts">
/**
 * App root.
 *
 * Three mutually-exclusive render states:
 * 1. **Loading** — branded splash while workspace initializes
 * 2. **Welcome** — no workspace configured or saved path is stale
 * 3. **Ready** — workspace loaded, normal layout renders
 *
 * The native File menu is always available once the app is ready,
 * even during the welcome screen. Its actions transition state
 * from welcome → ready via hasWorkspace.
 */
import { invoke } from "@tauri-apps/api/core"
import { Menu, MenuItem, Submenu } from "@tauri-apps/api/menu"
import { nextTick, watch } from "vue"
import WelcomeScreen from "~/components/WelcomeScreen.vue"
import { useFileSystem } from "~/composables/useFileSystem"
import { createGraphService } from "~/services/graph"
import { setupComarkComponents } from "~/lib/comark-setup"
import type { InitResult } from "~/stores/workspace"
import { useWorkspaceStore } from "~/stores/workspace"

const workspace = useWorkspaceStore()
const fs = useFileSystem()

const ready = ref(false)
const hasWorkspace = ref(false)
const stalePath = ref<string | null>(null)

async function handleMenuNewWorkspace() {
  const path = await workspace.pickWorkspace()
  if (!path) return
  await workspace.setWorkspace(path)
  hasWorkspace.value = true
}

async function handleMenuOpenWorkspace() {
  const path = await workspace.pickWorkspace()
  if (!path) return
  await workspace.switchWorkspace(path)
  hasWorkspace.value = true
}

async function handleMenuOpenRecent(path: string) {
  await workspace.switchWorkspace(path)
  hasWorkspace.value = true
}

async function handleCloseWorkspace() {
  await workspace.closeWorkspace()
  stalePath.value = null
  hasWorkspace.value = false
}

async function handleReindex() {
  const toast = useToast()
  const { id } = toast.add({
    title: "Rebuilding index…",
    duration: 0,
    close: false,
    color: "neutral",
  })
  try {
    await workspace.reindex()
    toast.update(id, {
      title: "Index rebuilt",
      color: "success",
      duration: 3000,
      close: true,
    })
  } catch {
    toast.update(id, {
      title: "Indexing failed",
      color: "error",
      duration: 5000,
      close: true,
    })
  }
}

async function handleQuit() {
  await invoke("exit_app")
}

async function buildMenu() {
  const fileItems: (MenuItem | Submenu | { item: "Separator" })[] = [
    await MenuItem.new({
      id: "new-workspace",
      text: "New",
      accelerator: "CmdOrCtrl+Shift+N",
      action: handleMenuNewWorkspace,
    }),
    await MenuItem.new({
      id: "open-workspace",
      text: "Open",
      accelerator: "CmdOrCtrl+O",
      action: handleMenuOpenWorkspace,
    }),
    { item: "Separator" },
  ]

  // Recent workspaces as a submenu
  const recent = workspace.recentWorkspaces
  const validItems: MenuItem[] = []
  for (const p of recent) {
    if (await fs.pathExists(p)) {
      validItems.push(
        await MenuItem.new({
          id: `recent:${p}`,
          text: p.split("/").pop() ?? p,
          action: () => handleMenuOpenRecent(p),
        }),
      )
    }
  }
  if (validItems.length > 0) {
    const recentSubmenu = await Submenu.new({
      text: "Recent",
      items: validItems,
    })
    fileItems.push(recentSubmenu)
  }

  fileItems.push(
    { item: "Separator" },
    await MenuItem.new({
      id: "close-workspace",
      text: "Close",
      accelerator: "CmdOrCtrl+W",
      action: handleCloseWorkspace,
    }),
    { item: "Separator" },
    await MenuItem.new({
      id: "reindex-library",
      text: "Reindex Library",
      accelerator: "CmdOrCtrl+Shift+R",
      action: handleReindex,
    }),
    { item: "Separator" },
    await MenuItem.new({
      id: "quit",
      text: "Quit Enesis",
      accelerator: "CmdOrCtrl+Q",
      action: handleQuit,
    }),
  )

  const fileSubmenu = await Submenu.new({
    text: "File",
    items: fileItems,
  })

  const menu = await Menu.new({ items: [fileSubmenu] })
  await menu.setAsAppMenu()
}

onMounted(async () => {
  const result: InitResult = await workspace.initialize()
  if (result.status === "ok") {
    hasWorkspace.value = true
  } else if (result.status === "stale") {
    stalePath.value = result.path
  }
  await workspace.loadRecentWorkspaces()
  ready.value = true
})

// Remove the SPA splash overlay once the view has rendered
watch(ready, (isReady) => {
  if (isReady) nextTick(() => document.getElementById("splash")?.remove())
}, { once: true })

// Build the menu once the app is ready — always visible
watch(
  ready,
  async (r) => {
    if (r) await buildMenu()
  },
  { once: true },
)

// Rebuild menu when recent workspaces change
watch(
  () => workspace.recentWorkspaces,
  async () => {
    if (ready.value) await buildMenu()
  },
  { deep: true },
)

// Set up Comark components when db becomes available after init
watch(
  () => workspace.db,
  (db) => {
    if (db && workspace.workspacePath) {
      setupComarkComponents(createGraphService(db))
    }
  },
)
</script>

<template>
  <UApp>
    <!-- 1. Needs onboarding — no workspace or stale path -->
    <WelcomeScreen
      v-if="ready && !hasWorkspace"
      :stale-path="stalePath"
      :recent-paths="workspace.recentWorkspaces"
      @complete="hasWorkspace = true"
    />

    <!-- 2. Ready — normal app chrome -->
    <NuxtLayout v-else-if="ready && hasWorkspace">
      <NuxtPage :key="workspace.workspacePath" />
    </NuxtLayout>

    <!-- 3. Initializing — nothing rendered (SPA template covers this phase) -->
  </UApp>
</template>
