import { describe, it, expect, vi, beforeEach } from 'vitest'
import { renderHook, act } from '@testing-library/react'
import type { VaultEntry } from '../types'
import { useEntryActions } from './useEntryActions'

const makeEntry = (overrides: Partial<VaultEntry> = {}): VaultEntry => ({
  path: '/vault/note/test.md',
  filename: 'test.md',
  title: 'Test Note',
  isA: 'Note',
  aliases: [],
  belongsTo: [],
  relatedTo: [],
  status: 'Active',
  owner: null,
  cadence: null,
  archived: false,
  trashed: false,
  trashedAt: null,
  modifiedAt: 1700000000,
  createdAt: 1700000000,
  fileSize: 100,
  snippet: '',
  wordCount: 0,
  relationships: {},
  icon: null,
  color: null,
  order: null,
  outgoingLinks: [],
  ...overrides,
})

describe('useEntryActions', () => {
  const updateEntry = vi.fn()
  const handleUpdateFrontmatter = vi.fn().mockResolvedValue(undefined)
  const handleDeleteProperty = vi.fn().mockResolvedValue(undefined)
  const setToastMessage = vi.fn()

  function setup(entries: VaultEntry[] = []) {
    return renderHook(() =>
      useEntryActions({
        entries,
        updateEntry,
        handleUpdateFrontmatter,
        handleDeleteProperty,
        setToastMessage,
      })
    )
  }

  beforeEach(() => {
    vi.clearAllMocks()
  })

  describe('handleTrashNote', () => {
    it('sets trashed frontmatter and updates entry state', async () => {
      const { result } = setup()

      await act(async () => {
        await result.current.handleTrashNote('/vault/note/test.md')
      })

      expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/note/test.md', 'trashed', true)
      expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/note/test.md', 'trashed_at', expect.stringMatching(/^\d{4}-\d{2}-\d{2}$/))
      expect(updateEntry).toHaveBeenCalledWith('/vault/note/test.md', {
        trashed: true,
        trashedAt: expect.any(Number),
      })
      expect(setToastMessage).toHaveBeenCalledWith('Note moved to trash')
    })
  })

  describe('handleRestoreNote', () => {
    it('clears trashed frontmatter and updates entry state', async () => {
      const { result } = setup()

      await act(async () => {
        await result.current.handleRestoreNote('/vault/note/test.md')
      })

      expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/note/test.md', 'trashed', false)
      expect(handleDeleteProperty).toHaveBeenCalledWith('/vault/note/test.md', 'trashed_at')
      expect(updateEntry).toHaveBeenCalledWith('/vault/note/test.md', {
        trashed: false,
        trashedAt: null,
      })
      expect(setToastMessage).toHaveBeenCalledWith('Note restored from trash')
    })
  })

  describe('handleArchiveNote', () => {
    it('sets archived frontmatter and updates entry state', async () => {
      const { result } = setup()

      await act(async () => {
        await result.current.handleArchiveNote('/vault/note/test.md')
      })

      expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/note/test.md', 'archived', true)
      expect(updateEntry).toHaveBeenCalledWith('/vault/note/test.md', { archived: true })
      expect(setToastMessage).toHaveBeenCalledWith('Note archived')
    })
  })

  describe('handleUnarchiveNote', () => {
    it('clears archived frontmatter and updates entry state', async () => {
      const { result } = setup()

      await act(async () => {
        await result.current.handleUnarchiveNote('/vault/note/test.md')
      })

      expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/note/test.md', 'archived', false)
      expect(updateEntry).toHaveBeenCalledWith('/vault/note/test.md', { archived: false })
      expect(setToastMessage).toHaveBeenCalledWith('Note unarchived')
    })
  })

  describe('handleCustomizeType', () => {
    it('updates icon and color on the type entry', () => {
      const typeEntry = makeEntry({ isA: 'Type', title: 'Recipe', path: '/vault/type/recipe.md' })
      const { result } = setup([typeEntry])

      act(() => {
        result.current.handleCustomizeType('Recipe', 'cooking-pot', 'green')
      })

      expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/type/recipe.md', 'icon', 'cooking-pot')
      expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/type/recipe.md', 'color', 'green')
      expect(updateEntry).toHaveBeenCalledWith('/vault/type/recipe.md', { icon: 'cooking-pot', color: 'green' })
    })

    it('does nothing when type entry not found', () => {
      const { result } = setup([])

      act(() => {
        result.current.handleCustomizeType('NonExistent', 'star', 'red')
      })

      expect(handleUpdateFrontmatter).not.toHaveBeenCalled()
      expect(updateEntry).not.toHaveBeenCalled()
    })
  })

  describe('handleReorderSections', () => {
    it('updates order on multiple type entries', () => {
      const typeA = makeEntry({ isA: 'Type', title: 'Note', path: '/vault/type/note.md' })
      const typeB = makeEntry({ isA: 'Type', title: 'Project', path: '/vault/type/project.md' })
      const { result } = setup([typeA, typeB])

      act(() => {
        result.current.handleReorderSections([
          { typeName: 'Note', order: 0 },
          { typeName: 'Project', order: 1 },
        ])
      })

      expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/type/note.md', 'order', 0)
      expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/type/project.md', 'order', 1)
      expect(updateEntry).toHaveBeenCalledWith('/vault/type/note.md', { order: 0 })
      expect(updateEntry).toHaveBeenCalledWith('/vault/type/project.md', { order: 1 })
    })

    it('skips types that are not found', () => {
      const typeA = makeEntry({ isA: 'Type', title: 'Note', path: '/vault/type/note.md' })
      const { result } = setup([typeA])

      act(() => {
        result.current.handleReorderSections([
          { typeName: 'Note', order: 0 },
          { typeName: 'Missing', order: 1 },
        ])
      })

      // Only Note's order was set; Missing was skipped
      expect(handleUpdateFrontmatter).toHaveBeenCalledTimes(1)
      expect(handleUpdateFrontmatter).toHaveBeenCalledWith('/vault/type/note.md', 'order', 0)
      expect(updateEntry).toHaveBeenCalledTimes(1)
    })
  })
})
