import { slash } from '@antfu/utils'
import { relative } from 'pathe'
import { useCommand } from 'reactive-vscode'
import { ConfigurationTarget, window, workspace } from 'vscode'
import { useDevServer } from './composables/useDevServer'
import { useFocusedSlide } from './composables/useFocusedSlide'
import { config } from './configs'
import { activeEntry, activeProject, addProject, projects, rescanProjects } from './projects'
import { findPossibleEntries } from './utils/findPossibleEntries'
import { getSlidesTitle } from './utils/getSlidesTitle'
import { usePreviewWebview } from './views/previewWebview'

const RE_DIGITS = /^\d+$/

export function useCommands() {
  useCommand('slidev.enable-extension', () => config.update('force-enabled', true, ConfigurationTarget.Workspace))
  useCommand('slidev.disable-extension', () => config.update('force-enabled', false, ConfigurationTarget.Workspace))

  useCommand('slidev.rescan-projects', rescanProjects)

  useCommand('slidev.choose-entry', async () => {
    while (true) {
      const addNewEntry = '$(add) Add new slides entry...'
      const entry = await window.showQuickPick(
        [...projects.keys(), addNewEntry],
        {
          title: 'Choose active slides entry.',
        },
      )
      if (entry === addNewEntry) {
        if (!await addEntry()) {
          break
        }
      }
      else if (entry) {
        activeEntry.value = entry
        break
      }
      else {
        break
      }
    }
  })

  useCommand('slidev.add-entry', addEntry)

  async function addEntry() {
    const files = await findPossibleEntries()
    const selected = await window.showQuickPick(files, {
      title: 'Choose Markdown files to add as slides entries.',
      canPickMany: true,
    })
    if (selected) {
      for (const entry of selected)
        await addProject(entry)
      if (workspace.workspaceFolders) {
        const workspaceRoot = workspace.workspaceFolders[0].uri.fsPath
        const relatives = selected.map(s => slash(relative(workspaceRoot, s)))
        // write back to settings.json
        await config.update('include', [...config.include, ...relatives])
      }
    }
    return !!selected
  }

  useCommand('slidev.remove-entry', async (node: any) => {
    const entry = slash(node.treeItem.resourceUri.fsPath)
    if (activeEntry.value === entry)
      activeEntry.value = null
    projects.delete(entry)
  })

  useCommand('slidev.set-as-active', async (node: any) => {
    const entry = slash(node.treeItem.resourceUri.fsPath)
    activeEntry.value = entry
  })

  useCommand('slidev.goto', (filepath: string, index: number) => {
    const { gotoSlide } = useFocusedSlide()
    gotoSlide(filepath, index)
  })
  useCommand('slidev.next', () => {
    const { focusedMarkdown, focusedSourceSlide, gotoSlide } = useFocusedSlide()
    if (!focusedMarkdown.value || focusedSourceSlide.value == null)
      return
    gotoSlide(focusedMarkdown.value.filepath, focusedSourceSlide.value.index + 1)
  })
  useCommand('slidev.prev', () => {
    const { focusedMarkdown, focusedSourceSlide, gotoSlide } = useFocusedSlide()
    if (!focusedMarkdown.value || focusedSourceSlide.value == null)
      return
    gotoSlide(focusedMarkdown.value.filepath, focusedSourceSlide.value.index - 1)
  })

  useCommand('slidev.refresh-preview', async () => {
    const { refresh } = usePreviewWebview()
    await refresh()
  })

  useCommand('slidev.config-port', async () => {
    if (!activeProject.value) {
      window.showErrorMessage('No active project to configure port.')
      return
    }
    const port = await window.showInputBox({
      prompt: `Slidev Preview Port for ${getSlidesTitle(activeProject.value.data)}`,
      value: config.port.toString(),
      validateInput: (v) => {
        if (!RE_DIGITS.test(v))
          return 'Port should be a number'
        if (+v < 1024 || +v > 65535)
          return 'Port should be between 1024 and 65535'
        return null
      },
    })
    if (port && activeProject.value) {
      activeProject.value.port.value = +port
    }
  })

  useCommand('slidev.start-dev', async () => {
    const project = activeProject.value
    if (!project) {
      window.showErrorMessage('Cannot start dev server: No active slides project.')
      return
    }

    const { start, terminal } = useDevServer(project)
    start()
    terminal.value?.show()
  })

  useCommand('slidev.open-in-browser', () => usePreviewWebview().openExternal())

  useCommand('slidev.preview-prev-click', () => usePreviewWebview().prevClick())
  useCommand('slidev.preview-next-click', () => usePreviewWebview().nextClick())
  useCommand('slidev.preview-prev-slide', () => usePreviewWebview().prevSlide())
  useCommand('slidev.preview-next-slide', () => usePreviewWebview().nextSlide())
  useCommand('slidev.show-slide-preview', () => usePreviewWebview().setPreviewMode('slide'))
  useCommand('slidev.show-overview-preview', () => usePreviewWebview().setPreviewMode('overview'))

  useCommand('slidev.enable-preview-sync', () => (config.update('preview-sync', true, ConfigurationTarget.Global)))
  useCommand('slidev.disable-preview-sync', () => (config.update('preview-sync', false, ConfigurationTarget.Global)))
}
