import { describe, it, expect, vi, beforeEach } from 'vitest'

// Mock external dependencies before importing store
vi.mock('../../db/tabRepository', () => ({
  tabRepository: {
    loadAll: vi.fn().mockResolvedValue([]),
    loadActiveTabId: vi.fn().mockResolvedValue(null),
    saveAll: vi.fn().mockResolvedValue(undefined),
    saveActiveTabId: vi.fn().mockResolvedValue(undefined),
    clearAll: vi.fn().mockResolvedValue(undefined),
  },
}))

vi.mock('nanoid', () => {
  let counter = 0
  return {
    nanoid: vi.fn(() => `test-id-${++counter}`),
  }
})

import { useTabStore } from '../tabStore'

describe('tabStore', () => {
  beforeEach(() => {
    // Reset store state between tests
    useTabStore.setState({
      tabs: [],
      activeTabId: null,
      mruStack: [],
      _loaded: false,
    })
  })

  describe('createTab', () => {
    it('should create a new tab with default values', () => {
      const tab = useTabStore.getState().createTab()
      const state = useTabStore.getState()

      expect(tab.id).toBeTruthy()
      expect(tab.filePath).toBeNull()
      expect(tab.content).toBe('')
      expect(tab.isModified).toBe(false)
      expect(state.tabs).toHaveLength(1)
      expect(state.activeTabId).toBe(tab.id)
    })

    it('should create a tab with file path and content', () => {
      const tab = useTabStore.getState().createTab('/test.md', '# Hello')

      expect(tab.filePath).toBe('/test.md')
      expect(tab.content).toBe('# Hello')
    })

    it('should switch to existing tab if same filePath is opened', () => {
      useTabStore.getState().createTab('/test.md', '# First')

      // Create another tab to make it active
      useTabStore.getState().createTab('/other.md', '# Other')

      // Now try to open the first file again
      const existing = useTabStore.getState().createTab('/test.md', '# Updated')
      const state = useTabStore.getState()

      expect(state.tabs).toHaveLength(2)
      expect(state.activeTabId).toBe(existing.id)
    })
  })

  describe('closeTab', () => {
    it('should close a tab and update activeTabId', () => {
      const { createTab, closeTab } = useTabStore.getState()
      const tab1 = createTab('/file1.md')
      const tab2 = createTab('/file2.md')

      closeTab(tab1.id)
      const state = useTabStore.getState()

      expect(state.tabs).toHaveLength(1)
      expect(state.tabs[0].id).toBe(tab2.id)
      expect(state.activeTabId).toBe(tab2.id)
    })

    it('should set activeTabId to null when closing last tab', () => {
      const { createTab, closeTab } = useTabStore.getState()
      const tab = createTab('/file1.md')

      closeTab(tab.id)
      const state = useTabStore.getState()

      expect(state.tabs).toHaveLength(0)
      expect(state.activeTabId).toBeNull()
    })

    it('should handle closing non-existent tab gracefully', () => {
      const { createTab, closeTab } = useTabStore.getState()
      createTab('/file1.md')

      closeTab('non-existent')
      const state = useTabStore.getState()

      expect(state.tabs).toHaveLength(1)
    })
  })

  describe('closeOtherTabs', () => {
    it('should close all tabs except the specified one', () => {
      const { createTab, closeOtherTabs } = useTabStore.getState()
      const tab1 = createTab('/file1.md')
      createTab('/file2.md')
      createTab('/file3.md')

      closeOtherTabs(tab1.id)
      const state = useTabStore.getState()

      expect(state.tabs).toHaveLength(1)
      expect(state.tabs[0].id).toBe(tab1.id)
      expect(state.activeTabId).toBe(tab1.id)
    })
  })

  describe('closeAllTabs', () => {
    it('should close all tabs', () => {
      const { createTab, closeAllTabs } = useTabStore.getState()
      createTab('/file1.md')
      createTab('/file2.md')

      closeAllTabs()
      const state = useTabStore.getState()

      expect(state.tabs).toHaveLength(0)
      expect(state.activeTabId).toBeNull()
      expect(state.mruStack).toHaveLength(0)
    })
  })

  describe('closeTabsToRight', () => {
    it('should close tabs to the right of the specified tab', () => {
      const { createTab, closeTabsToRight } = useTabStore.getState()
      const tab1 = createTab('/file1.md')
      createTab('/file2.md')
      createTab('/file3.md')

      closeTabsToRight(tab1.id)
      const state = useTabStore.getState()

      expect(state.tabs).toHaveLength(1)
      expect(state.tabs[0].id).toBe(tab1.id)
    })

    it('should keep active tab if it is in the remaining set', () => {
      const { createTab, switchToTab, closeTabsToRight } = useTabStore.getState()
      const tab1 = createTab('/file1.md')
      createTab('/file2.md')
      createTab('/file3.md')

      switchToTab(tab1.id)
      closeTabsToRight(tab1.id)
      const state = useTabStore.getState()

      expect(state.activeTabId).toBe(tab1.id)
    })
  })

  describe('switchToTab', () => {
    it('should switch active tab and update MRU stack', () => {
      const { createTab, switchToTab } = useTabStore.getState()
      const tab1 = createTab('/file1.md')
      const tab2 = createTab('/file2.md')

      switchToTab(tab1.id)
      const state = useTabStore.getState()

      expect(state.activeTabId).toBe(tab1.id)
      expect(state.mruStack).toContain(tab2.id)
    })

    it('should not update if switching to already active tab', () => {
      const { createTab, switchToTab } = useTabStore.getState()
      createTab('/file1.md')

      const stateBefore = useTabStore.getState()
      switchToTab(stateBefore.activeTabId!)
      const stateAfter = useTabStore.getState()

      expect(stateAfter.mruStack).toEqual(stateBefore.mruStack)
    })
  })

  describe('updateTabContent', () => {
    it('should update content and mark as modified', () => {
      const { createTab, updateTabContent } = useTabStore.getState()
      const tab = createTab('/file.md', '# Old')

      updateTabContent(tab.id, '# New Content')
      const state = useTabStore.getState()
      const updated = state.tabs.find(t => t.id === tab.id)!

      expect(updated.content).toBe('# New Content')
      expect(updated.isModified).toBe(true)
    })
  })

  describe('getActiveTab', () => {
    it('should return the active tab', () => {
      const { createTab, getActiveTab } = useTabStore.getState()
      const tab = createTab('/file.md')

      const active = getActiveTab()
      expect(active?.id).toBe(tab.id)
    })

    it('should return null when no tabs exist', () => {
      const { getActiveTab } = useTabStore.getState()
      expect(getActiveTab()).toBeNull()
    })
  })

  describe('setModified', () => {
    it('should set modified flag on a tab', () => {
      const { createTab, setModified } = useTabStore.getState()
      const tab = createTab('/file.md')

      setModified(tab.id, true)
      const state = useTabStore.getState()
      const updated = state.tabs.find(t => t.id === tab.id)!

      expect(updated.isModified).toBe(true)

      setModified(tab.id, false)
      const state2 = useTabStore.getState()
      const updated2 = state2.tabs.find(t => t.id === tab.id)!

      expect(updated2.isModified).toBe(false)
    })
  })

  describe('updateTabScroll', () => {
    it('should update scroll position', () => {
      const { createTab, updateTabScroll } = useTabStore.getState()
      const tab = createTab('/file.md')

      updateTabScroll(tab.id, { scrollTop: 100, selectionStart: 10, selectionEnd: 20 })
      const state = useTabStore.getState()
      const updated = state.tabs.find(t => t.id === tab.id)!

      expect(updated.scrollTop).toBe(100)
      expect(updated.selectionStart).toBe(10)
      expect(updated.selectionEnd).toBe(20)
    })
  })

  describe('updateTabContent same-content guard (A3)', () => {
    it('should not mark as modified when content is unchanged', () => {
      const { createTab, updateTabContent } = useTabStore.getState()
      const tab = createTab('/file.md', '# Same Content')

      updateTabContent(tab.id, '# Same Content')
      const state = useTabStore.getState()
      const updated = state.tabs.find(t => t.id === tab.id)!

      expect(updated.content).toBe('# Same Content')
      expect(updated.isModified).toBe(false)
    })

    it('should mark as modified when content changes', () => {
      const { createTab, updateTabContent } = useTabStore.getState()
      const tab = createTab('/file.md', '# Old')

      updateTabContent(tab.id, '# New')
      const state = useTabStore.getState()
      const updated = state.tabs.find(t => t.id === tab.id)!

      expect(updated.content).toBe('# New')
      expect(updated.isModified).toBe(true)
    })
  })

  describe('moveTab (D1)', () => {
    it('should move tab to a new position', () => {
      const { createTab, moveTab } = useTabStore.getState()
      const tab1 = createTab('/file1.md')
      const tab2 = createTab('/file2.md')
      const tab3 = createTab('/file3.md')

      // tab1 移到末尾：期望 [tab2, tab3, tab1]
      moveTab(tab1.id, 2)
      const state = useTabStore.getState()

      // 用引用相等而非基于全局计数器 ID
      expect(state.tabs[0]).toBe(tab2)
      expect(state.tabs[1]).toBe(tab3)
      expect(state.tabs[2]).toBe(tab1)
    })

    it('should handle moving to same position (no-op)', () => {
      const { createTab, moveTab } = useTabStore.getState()
      const tab1 = createTab('/file1.md')
      createTab('/file2.md')

      moveTab(tab1.id, 0)
      const state = useTabStore.getState()

      expect(state.tabs[0].id).toBe(tab1.id)
      expect(state.tabs.length).toBe(2)
    })

    it('should handle moving non-existent tab gracefully', () => {
      const { createTab, moveTab } = useTabStore.getState()
      createTab('/file1.md')

      moveTab('non-existent', 0)
      const state = useTabStore.getState()

      expect(state.tabs.length).toBe(1)
    })
  })
})