import { create } from 'zustand'
import { nanoid } from 'nanoid'
import type { Tab } from '../types/tab'
import { tabRepository } from '../db/tabRepository'

// PF-08: 防抖工具函数
function debounce<F extends (...args: unknown[]) => void>(fn: F, delay: number): F & { cancel: () => void } {
  let timer: ReturnType<typeof setTimeout> | null = null
  const debounced = ((...args: unknown[]) => {
    if (timer) clearTimeout(timer)
    timer = setTimeout(() => {
      fn(...args)
      timer = null
    }, delay)
  }) as F & { cancel: () => void }
  debounced.cancel = () => {
    if (timer) {
      clearTimeout(timer)
      timer = null
    }
  }
  return debounced
}

interface TabState {
  tabs: Tab[]
  activeTabId: string | null
  mruStack: string[]
  _loaded: boolean

  createTab: (filePath?: string | null, content?: string) => Tab
  closeTab: (tabId: string) => void
  closeOtherTabs: (tabId: string) => void
  closeAllTabs: () => void
  closeTabsToRight: (tabId: string) => void
  moveTab: (fromId: string, toIndex: number) => void
  switchToTab: (tabId: string) => void
  updateTabContent: (tabId: string, content: string) => void
  setModified: (tabId: string, modified: boolean) => void
  getActiveTab: () => Tab | null
  updateTabScroll: (tabId: string, scroll: Partial<Pick<Tab, 'scrollTop' | 'selectionStart' | 'selectionEnd'>>) => void
  loadFromDB: () => Promise<void>
  saveToDB: () => Promise<void>
}

// PF-08: 模块级防抖保存函数（500ms延迟）
let _debouncedSaveToDB: (() => void) & { cancel: () => void } | null = null

function getActualSaveToDB(get: () => TabState) {
  return async () => {
    const { tabs } = get()
    if (tabs.length === 0) {
      await tabRepository.clearAll()
      return
    }
    const snapshots = tabs.map(t => ({
      id: t.id,
      filePath: t.filePath,
      content: t.content,
      isModified: t.isModified,
      scrollTop: t.scrollTop,
      selectionStart: t.selectionStart,
      selectionEnd: t.selectionEnd,
      updatedAt: Date.now()
    }))
    await tabRepository.saveAll(snapshots)
    await tabRepository.saveActiveTabId(get().activeTabId)
  }
}

export const useTabStore = create<TabState>((set, get) => {
  const actualSave = getActualSaveToDB(get)
  _debouncedSaveToDB = debounce(actualSave, 500)

  return {
    tabs: [],
    activeTabId: null,
    mruStack: [],
    _loaded: false,

    loadFromDB: async () => {
      if (get()._loaded) return
      try {
        const [snapshots, savedActiveTabId] = await Promise.all([
          tabRepository.loadAll(),
          tabRepository.loadActiveTabId()
        ])
        if (snapshots.length > 0) {
          const tabs: Tab[] = snapshots.map(s => ({
            id: s.id,
            filePath: s.filePath,
            content: s.content,
            isModified: s.isModified,
            scrollTop: s.scrollTop,
            selectionStart: s.selectionStart,
            selectionEnd: s.selectionEnd
          }))
          const activeTabId = (savedActiveTabId && tabs.find(t => t.id === savedActiveTabId))
            ? savedActiveTabId
            : tabs[tabs.length - 1].id
          set({ tabs, activeTabId, _loaded: true })
        } else {
          set({ _loaded: true })
        }
      } catch (err) {
        // eslint-disable-next-line no-console -- DB load error
        console.error('Failed to load tabs from DB:', err)
        set({ _loaded: true })
      }
    },

    saveToDB: async () => {
      if (_debouncedSaveToDB) {
        _debouncedSaveToDB()
      }
    },

    createTab: (filePath: string | null = null, content: string = ''): Tab => {
      if (filePath) {
        const existing = get().tabs.find(t => t.filePath === filePath)
        if (existing) {
          get().switchToTab(existing.id)
          return existing
        }
      }

      const tab: Tab = {
        id: nanoid(),
        filePath,
        content,
        isModified: false,
        scrollTop: 0,
        selectionStart: 0,
        selectionEnd: 0
      }

      set(state => ({
        tabs: [...state.tabs, tab],
        activeTabId: tab.id
      }))

      _debouncedSaveToDB?.()
      return tab
    },

    closeTab: (tabId: string) => {
      set(state => {
        const index = state.tabs.findIndex(t => t.id === tabId)
        if (index === -1) return state

        const newTabs = state.tabs.filter(t => t.id !== tabId)
        const newMru = state.mruStack.filter(id => id !== tabId)

        let newActiveId = state.activeTabId
        if (state.activeTabId === tabId) {
          if (newTabs.length === 0) {
            newActiveId = null
          } else {
            const mruCandidate = newMru.find(id => newTabs.some(t => t.id === id))
            if (mruCandidate) {
              newActiveId = mruCandidate
              newMru.splice(newMru.indexOf(mruCandidate), 1)
            } else {
              const newIndex = Math.min(index, newTabs.length - 1)
              newActiveId = newTabs[newIndex].id
            }
          }
        }

        return { tabs: newTabs, activeTabId: newActiveId, mruStack: newMru }
      })

      _debouncedSaveToDB?.()
    },

    closeOtherTabs: (tabId: string) => {
      set(state => {
        const target = state.tabs.find(t => t.id === tabId)
        if (!target) return state
        return { tabs: [target], activeTabId: tabId, mruStack: [] }
      })
      _debouncedSaveToDB?.()
    },

    closeAllTabs: () => {
      set({ tabs: [], activeTabId: null, mruStack: [] })
      _debouncedSaveToDB?.()
    },

    closeTabsToRight: (tabId: string) => {
      set(state => {
        const index = state.tabs.findIndex(t => t.id === tabId)
        if (index === -1) return state
        const newTabs = state.tabs.slice(0, index + 1)
        const newActiveId = state.activeTabId && newTabs.find(t => t.id === state.activeTabId)
          ? state.activeTabId
          : tabId
        return {
          tabs: newTabs,
          activeTabId: newActiveId,
          mruStack: state.mruStack.filter(id => newTabs.some(t => t.id === id))
        }
      })
      _debouncedSaveToDB?.()
    },

    // D1: 拖拽排序 — 将 fromId 标签移动到新的数组位置
    moveTab: (fromId: string, toIndex: number) => {
      set(state => {
        const fromIndex = state.tabs.findIndex(t => t.id === fromId)
        if (fromIndex === -1 || fromIndex === toIndex) return state
        const newTabs = [...state.tabs]
        const [moved] = newTabs.splice(fromIndex, 1)
        // 移除后直接插入目标位置即可（splice 在移除元素后数组已缩短，
        // toIndex 相对于原数组的位置在移除后无需调整）
        newTabs.splice(toIndex, 0, moved)
        return { tabs: newTabs }
      })
      _debouncedSaveToDB?.()
    },

    switchToTab: (tabId: string) => {
      set(state => {
        if (state.activeTabId === tabId) return state
        const newMru = state.activeTabId
          ? [state.activeTabId, ...state.mruStack.filter(id => id !== state.activeTabId)]
          : state.mruStack
        return { activeTabId: tabId, mruStack: newMru }
      })
      setTimeout(() => tabRepository.saveActiveTabId(tabId), 0)
    },

    updateTabContent: (tabId: string, content: string) => {
      set(state => ({
        tabs: state.tabs.map(t => {
          if (t.id !== tabId) return t
          // A3: 内容未变时不变更对象引用（避免误标记 isModified）
          if (t.content === content) return t
          return { ...t, content, isModified: true }
        })
      }))
    },

    setModified: (tabId: string, modified: boolean) => {
      set(state => ({
        tabs: state.tabs.map(t =>
          t.id === tabId ? { ...t, isModified: modified } : t
        )
      }))
    },

    getActiveTab: (): Tab | null => {
      const { tabs, activeTabId } = get()
      return tabs.find(t => t.id === activeTabId) ?? null
    },

    updateTabScroll: (tabId: string, scroll: Partial<Pick<Tab, 'scrollTop' | 'selectionStart' | 'selectionEnd'>>) => {
      set(state => ({
        tabs: state.tabs.map(t =>
          t.id === tabId ? { ...t, ...scroll } : t
        )
      }))
    }
  }
})

// D5: 立即 flush 待保存的标签状态到 IndexedDB（取消防抖后同步写入）
export function flushSaveToDB(): Promise<void> {
  _debouncedSaveToDB?.cancel()
  return getActualSaveToDB(useTabStore.getState)()
}
