import { createPinia, setActivePinia } from "pinia"
import { beforeEach, describe, expect, it, vi } from "vitest"
import { useWorkspaceStore } from "./workspace"

// Mock the indexer — its dependency on @enesis/editor pulls in Vue files
// that vitest can't parse without a Vue plugin.
vi.mock("~/lib/indexer", () => ({
  initIndex: vi.fn(() =>
    Promise.resolve({
      execute: vi.fn(() => Promise.resolve()),
      select: vi.fn(() => Promise.resolve([])),
    }),
  ),
  fullReindex: vi.fn(() => Promise.resolve()),
  syncIndex: vi.fn(() => Promise.resolve()),
  indexFile: vi.fn(() => Promise.resolve()),
  removeFile: vi.fn(() => Promise.resolve()),
}))

// Mock Tauri core APIs
vi.mock("@tauri-apps/api/core", () => ({
  invoke: vi.fn(),
  transformCallback: vi.fn(),
  convertFileSrc: vi.fn(),
}))

// Mock Tauri event system — listen() is used by initializeIndex
vi.mock("@tauri-apps/api/event", () => ({
  listen: vi.fn(() => Promise.resolve(() => {})),
}))

// Mock Tauri dialog plugin
vi.mock("@tauri-apps/plugin-dialog", () => ({
  open: vi.fn(),
}))

// Mock Tauri store plugin — useSettings imports it at module level,
// but falls back to in-memory Map outside Tauri. The mock prevents
// import errors in the test environment.
vi.mock("@tauri-apps/plugin-store", () => ({
  Store: {
    load: vi.fn(() =>
      Promise.resolve({ get: vi.fn(), set: vi.fn(), save: vi.fn() }),
    ),
  },
}))

describe("workspace store", () => {
  beforeEach(() => {
    setActivePinia(createPinia())
    vi.clearAllMocks()
  })

  describe("initialize", () => {
    it("returns missing when no saved workspace path", async () => {
      const store = useWorkspaceStore()
      const result = await store.initialize()
      expect(result).toEqual({ status: "missing" })
    })

    it("returns stale when stored path no longer exists", async () => {
      const { invoke } = await import("@tauri-apps/api/core")
      vi.mocked(invoke).mockRejectedValueOnce(new Error("not found"))

      // Set a stored path via the in-memory settings store
      const store = useWorkspaceStore()
      // Need to set the path through settings, which is internal.
      // We'll work around by accessing settings through the store's closure
      // or by setting up the state directly through the store's internal functions.
      //
      // Instead, we set up the setting before the store initializes
      const { useSettings } = await import("~/composables/useSettings")
      const settings = useSettings()
      await settings.set("workspacePath", "/some/stale/path")

      const result = await store.initialize()

      expect(result).toEqual({
        status: "stale",
        path: "/some/stale/path",
      })
      // Should not have set workspacePath on the store
      expect(store.workspacePath).toBeNull()
    })

    it("returns ok when stored path exists", async () => {
      const { invoke } = await import("@tauri-apps/api/core")
      vi.mocked(invoke)
        .mockResolvedValueOnce(["/existing/pages/notes.md"]) // list_directory check
        .mockResolvedValueOnce(["/existing/pages/notes.md"]) // scanFiles list_directory
        .mockResolvedValueOnce("") // start_watcher
        .mockResolvedValueOnce(undefined)

      const { useSettings } = await import("~/composables/useSettings")
      const settings = useSettings()
      await settings.set("workspacePath", "/existing")

      const store = useWorkspaceStore()
      const result = await store.initialize()

      expect(result).toEqual({ status: "ok" })
      expect(store.workspacePath).toBe("/existing")
    })
  })

  describe("pickWorkspace", () => {
    it("returns selected path from the dialog", async () => {
      const { open } = await import("@tauri-apps/plugin-dialog")
      vi.mocked(open).mockResolvedValue("/selected/path")

      const store = useWorkspaceStore()
      const result = await store.pickWorkspace()
      expect(result).toBe("/selected/path")
    })

    it("returns null when user cancels the dialog", async () => {
      const { open } = await import("@tauri-apps/plugin-dialog")
      vi.mocked(open).mockResolvedValue(null)

      const store = useWorkspaceStore()
      const result = await store.pickWorkspace()
      expect(result).toBeNull()
    })
  })

  describe("setWorkspace", () => {
    it("sets up the workspace at the given path", async () => {
      const { invoke } = await import("@tauri-apps/api/core")
      // useFileSystem composable skips invoke calls outside Tauri
      // via isTauri() guard. Only the direct invoke call in
      // initializeIndex (start_watcher) reaches the mock.
      vi.mocked(invoke).mockResolvedValue(undefined)

      const store = useWorkspaceStore()
      await store.setWorkspace("/new/workspace")

      // Core state is set
      expect(store.workspacePath).toBe("/new/workspace")

      // Settings are persisted
      const settings = await import("~/composables/useSettings").then((m) =>
        m.useSettings(),
      )
      const saved = await settings.get<string>("workspacePath")
      expect(saved).toBe("/new/workspace")

      // File watcher was started
      expect(invoke).toHaveBeenCalledWith("start_watcher", {
        workspacePath: "/new/workspace",
      })
    })
  })
})
