/**
 * Workspace Pinia store.
 * Single source of truth for workspace state across all components.
 */

import { invoke } from "@tauri-apps/api/core"
import { listen } from "@tauri-apps/api/event"
import { open } from "@tauri-apps/plugin-dialog"
import type Database from "@tauri-apps/plugin-sql"
import { defineStore } from "pinia"
import { useFileSystem } from "~/composables/useFileSystem"
import { useSettings } from "~/composables/useSettings"
import { fullReindex, indexFile, initIndex, removeFile } from "~/lib/indexer"

// Track files the app wrote itself, to avoid re-indexing on our own writes
const inFlightWrites = new Set<string>()

export function markInFlight(path: string) {
  inFlightWrites.add(path)
  setTimeout(() => inFlightWrites.delete(path), 500)
}

export interface WorkspaceFile {
  path: string
  name: string
  isJournal: boolean
  date?: string
}

export const useWorkspaceStore = defineStore("workspace", () => {
  const workspacePath = ref<string | null>(null)
  const files = ref<WorkspaceFile[]>([])
  const currentFilePath = ref<string | null>(null)
  const loading = ref(false)
  const db = ref<Database | null>(null)

  const { readFile, writeFile, listDirectory, createDirectory } =
    useFileSystem()
  const settings = useSettings()

  const journals = computed(() => files.value.filter((f) => f.isJournal))
  const pages = computed(() => files.value.filter((f) => !f.isJournal))

  async function initializeIndex(path: string) {
    db.value = await initIndex(`${path}/index.db`)
    await fullReindex(
      { listDirectory, readFile, workspacePath: path },
      db.value,
    )

    // Start file watcher
    try {
      await invoke("start_watcher", { workspacePath: path })
    } catch (e) {
      console.warn("[workspace] failed to start file watcher:", e)
    }

    // Listen for file changes from the watcher
    const _unlisten = await listen<{ path: string; kind: string }>(
      "fs-watch:change",
      async (event) => {
        const { path: changedPath, kind } = event.payload

        // Ignore events from files we wrote ourselves
        if (
          inFlightWrites.has(changedPath) ||
          inFlightWrites.has(changedPath.replace("/./", "/"))
        ) {
          return
        }

        if (!db.value) return

        try {
          if (kind === "deleted") {
            await removeFile(db.value, changedPath)
          } else {
            const content = await readFile(changedPath)
            await indexFile(db.value, changedPath, content)
          }
          await scanFiles()
        } catch (e) {
          console.warn(
            "[workspace] watcher: failed to re-index",
            changedPath,
            e,
          )
        }
      },
    )
  }

  async function initialize(): Promise<boolean> {
    const saved = await settings.get<string>("workspacePath")
    if (saved) {
      workspacePath.value = saved
      const sessionFile = await settings.get<string>("currentFilePath")
      if (sessionFile) currentFilePath.value = sessionFile
      await scanFiles()
      await initializeIndex(saved)
      return true
    }
    return false
  }

  async function pickWorkspace(): Promise<string | null> {
    const selected = await open({
      directory: true,
      multiple: false,
      title: "Select your workspace directory",
    })
    if (!selected) return null

    workspacePath.value = selected
    await settings.set("workspacePath", selected)
    await ensureDirectories()
    await scanFiles()
    await initializeIndex(selected)
    return selected
  }

  async function ensureDirectories() {
    if (!workspacePath.value) return
    await createDirectory(`${workspacePath.value}/journals`)
    await createDirectory(`${workspacePath.value}/pages`)
    await createDirectory(`${workspacePath.value}/templates`)
  }

  async function scanFiles() {
    if (!workspacePath.value) return
    loading.value = true
    try {
      const allFiles = await listDirectory(workspacePath.value)
      const markdownFiles = allFiles.filter((f) => f.endsWith(".md"))
      files.value = markdownFiles.map((p) => {
        const name = p.split("/").pop() ?? p
        const isJournal = !!name.match(/^\d{4}-\d{2}-\d{2}\.md$/)
        return {
          path: p,
          name,
          isJournal,
          date: isJournal ? name.replace(".md", "") : undefined,
        }
      })
    } finally {
      loading.value = false
    }
  }

  async function loadFile(relativePath: string): Promise<string> {
    if (!workspacePath.value) return ""
    return await readFile(`${workspacePath.value}/${relativePath}`)
  }

  async function saveFile(
    relativePath: string,
    content: string,
  ): Promise<void> {
    if (!workspacePath.value) return
    await writeFile(`${workspacePath.value}/${relativePath}`, content)
  }

  async function loadSavedFile(): Promise<string | null> {
    if (!currentFilePath.value || !workspacePath.value) return null
    try {
      return await readFile(currentFilePath.value)
    } catch {
      currentFilePath.value = null
      return null
    }
  }

  async function saveCurrentFile(content: string): Promise<void> {
    if (!currentFilePath.value || !workspacePath.value) return
    await writeFile(currentFilePath.value, content)
  }

  function openFile(relativePath: string) {
    if (!workspacePath.value) return
    currentFilePath.value = `${workspacePath.value}/${relativePath}`
    settings.set("currentFilePath", currentFilePath.value)
  }

  return {
    workspacePath,
    files,
    journals,
    pages,
    currentFilePath,
    loading,
    db,
    initialize,
    pickWorkspace,
    scanFiles,
    loadFile,
    saveFile,
    loadSavedFile,
    saveCurrentFile,
    openFile,
  }
})
