import type { Modifier } from 'obsidian'
import { MarkdownView, Modal } from 'obsidian'
import { mount, unmount } from 'svelte'
import { Action, eventBus, EventNames, isInputComposition } from '../globals'
import type LocatorPlugin from '../main'
import ModalVault from './ModalVault.svelte'

abstract class LocatorModal extends Modal {
  protected constructor(plugin: LocatorPlugin) {
    super(plugin.app)
    const settings = plugin.settings

    // Remove all the default modal's children
    // so that we can more easily customize it
    // const closeEl = this.containerEl.find('.modal-close-button')
    this.modalEl.replaceChildren()
    // this.modalEl.append(closeEl)
    this.modalEl.addClass('locator-modal', 'prompt')
    this.modalEl.removeClass('modal')
    this.modalEl.tabIndex = -1

    // Setup events that can be listened through the event bus

    // #region Up/Down navigation

    this.scope.register([], 'ArrowDown', e => {
      e.preventDefault()
      eventBus.emit(Action.ArrowDown)
    })
    this.scope.register([], 'ArrowUp', e => {
      e.preventDefault()
      eventBus.emit(Action.ArrowUp)
    })

    // Ctrl+j/k
    for (const key of [
      { k: 'J', dir: 'down' },
      { k: 'K', dir: 'up' },
    ] as const) {
      for (const modifier of ['Ctrl', 'Mod'] as const) {
        this.scope.register([modifier], key.k, _e => {
          if (settings.vimLikeNavigationShortcut) {
            // e.preventDefault()
            eventBus.emit('arrow-' + key.dir)
          }
        })
      }
    }

    // Ctrl+n/p
    for (const key of [
      { k: 'N', dir: 'down' },
      { k: 'P', dir: 'up' },
    ] as const) {
      for (const modifier of ['Ctrl', 'Mod'] as const) {
        this.scope.register([modifier], key.k, _e => {
          if (settings.vimLikeNavigationShortcut) {
            // e.preventDefault()
            eventBus.emit('arrow-' + key.dir)
          }
        })
      }
    }

    // #endregion Up/Down navigation

    let openInCurrentPaneKey: Modifier[]
    let openInNewPaneKey: Modifier[]
    let createInCurrentPaneKey: Modifier[]
    let createInNewPaneKey: Modifier[]
    let openInNewLeafKey: Modifier[] = ['Mod', 'Alt']
    if (settings.openInNewPane) {
      openInCurrentPaneKey = ['Mod']
      openInNewPaneKey = []
      createInCurrentPaneKey = ['Mod', 'Shift']
      createInNewPaneKey = ['Shift']
    } else {
      openInCurrentPaneKey = []
      openInNewPaneKey = ['Mod']
      createInCurrentPaneKey = ['Shift']
      createInNewPaneKey = ['Mod', 'Shift']
    }

    // Open in new pane
    this.scope.register(openInNewPaneKey, 'Enter', e => {
      e.preventDefault()
      eventBus.emit(Action.OpenInNewPane)
    })

    // Open in a new leaf
    this.scope.register(openInNewLeafKey, 'Enter', e => {
      e.preventDefault()
      eventBus.emit(Action.OpenInNewLeaf)
    })

    // Insert link
    this.scope.register(['Alt'], 'Enter', e => {
      e.preventDefault()
      eventBus.emit(Action.InsertLink)
    })

    // Create a new note
    this.scope.register(createInCurrentPaneKey, 'Enter', e => {
      e.preventDefault()
      eventBus.emit(Action.CreateNote)
    })
    this.scope.register(createInNewPaneKey, 'Enter', e => {
      e.preventDefault()
      eventBus.emit(Action.CreateNote, { newLeaf: true })
    })

    // Open in current pane
    this.scope.register(openInCurrentPaneKey, 'Enter', e => {
      if (!isInputComposition()) {
        // Check if the user is still typing
        e.preventDefault()
        eventBus.emit(Action.Enter)
      }
    })

    // Open in background
    this.scope.register(['Mod'], 'O', e => {
      if (!isInputComposition()) {
        // Check if the user is still typing
        e.preventDefault()
        eventBus.emit(Action.OpenInBackground)
      }
    })

    this.scope.register([], 'Tab', e => {
      e.preventDefault()
      eventBus.emit(Action.Tab) // Switch context
    })

    // Search history
    this.scope.register(['Alt'], 'ArrowDown', e => {
      e.preventDefault()
      eventBus.emit(Action.NextSearchHistory)
    })
    this.scope.register(['Alt'], 'ArrowUp', e => {
      e.preventDefault()
      eventBus.emit(Action.PrevSearchHistory)
    })

    // Context
    this.scope.register(['Mod'], 'G', _e => {
      eventBus.emit(EventNames.ToggleExcerpts)
    })
  }
}

export class LocatorVaultModal extends LocatorModal {
  /**
   * Instanciate the Locator vault modal
   * @param plugin
   * @param query The query to pre-fill the search field with
   */
  constructor(plugin: LocatorPlugin, query?: string) {
    super(plugin)

    // Selected text in the editor
    const selectedText = plugin.app.workspace
      .getActiveViewOfType(MarkdownView)
      ?.editor.getSelection()

    plugin.searchHistory.getHistory().then(history => {
      // Previously searched query
      const previous = history[0]

      // Instantiate and display the Svelte component
      const cmp = mount(ModalVault, {
        target: this.modalEl,
        props: {
          plugin,
          modal: this,
          previousQuery: query || selectedText || previous || '',
        },
      })

      this.onClose = () => {
        // Since the component is manually created,
        // we also need to manually destroy it
        unmount(cmp)
      }
    })
  }
}
