import type { MyReaderApi } from '../env'
import type {
  ListDirResult,
  OpenFileResult,
  OpenFolderResult,
  OpenImageResult,
  SaveDialogResult
} from '../types/ipc-types'
import {
  dirnamePath,
  isAbsDiskPath,
  joinAbsPath,
  normalizeAbsPath,
  resolvePathFromBaseFile
} from '../pathLite'
import { askDiskPath, rememberedAbsRoot } from './pathPrompt'
import { supportsFileSystemAccess, webVfs } from './webVfs'

const MD_PICKER = {
  description: 'Markdown',
  accept: { 'text/markdown': ['.md', '.markdown', '.mdown', '.mkd'] }
}

const IMAGE_PICKER = {
  description: '图片',
  accept: {
    'image/*': ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg', '.bmp', '.ico', '.avif']
  }
}

function pickFilesViaInput(accept: string, multiple: boolean): Promise<FileList | null> {
  return new Promise((resolve) => {
    const input = document.createElement('input')
    input.type = 'file'
    input.accept = accept
    input.multiple = multiple
    input.style.display = 'none'
    input.onchange = () => {
      resolve(input.files)
      input.remove()
    }
    input.oncancel = () => {
      resolve(null)
      input.remove()
    }
    document.body.appendChild(input)
    input.click()
  })
}

const menuHandlers = new Set<(action: string) => void>()

function emitMenu(action: string): void {
  for (const h of menuHandlers) h(action)
}

function installKeyboardShortcuts(): void {
  window.addEventListener('keydown', (e) => {
    if (!e.ctrlKey && !e.metaKey) return
    const k = e.key.toLowerCase()
    if (k === 'o' && !e.shiftKey) {
      e.preventDefault()
      emitMenu('file-open')
    } else if (k === 's' && e.shiftKey) {
      e.preventDefault()
      emitMenu('file-save-as')
    } else if (k === 's') {
      e.preventDefault()
      emitMenu('file-save')
    } else if (k === 't') {
      e.preventDefault()
      emitMenu('tab-new')
    } else if (k === 'w') {
      e.preventDefault()
      emitMenu('tab-close')
    } else if (k === 'e') {
      e.preventDefault()
      emitMenu('toggle-read')
    } else if (k === 'tab' && e.shiftKey) {
      e.preventDefault()
      emitMenu('tab-prev')
    } else if (k === 'tab') {
      e.preventDefault()
      emitMenu('tab-next')
    }
  })
}

let closeHandler: (() => void) | null = null
let closeAllowed = false

function installBeforeUnload(): void {
  window.addEventListener('beforeunload', (e) => {
    if (closeAllowed) return
    if (!closeHandler) return
    closeHandler()
    e.preventDefault()
    e.returnValue = ''
  })
}

async function promptFileAbsPath(fileName: string, suggested?: string): Promise<string | null> {
  const def = webVfs.getDefaultDirectory()
  const guess =
    suggested ??
    (isAbsDiskPath(def) ? joinAbsPath(def, fileName) : rememberedAbsRoot(fileName) ?? `F:\\${fileName}`)
  const raw = await askDiskPath({
    title: '请输入文件的本机完整路径',
    hint: `浏览器无法自动读取磁盘路径。请填写「${fileName}」的绝对路径（例如 F:\\coding\\notes\\${fileName}）。`,
    defaultValue: guess,
    folderName: fileName
  })
  return raw ? normalizeAbsPath(raw) : null
}

export function createBrowserApi(): MyReaderApi {
  installKeyboardShortcuts()
  installBeforeUnload()

  return {
    openFileDialog: async (): Promise<OpenFileResult> => {
      try {
        if (supportsFileSystemAccess() && window.showOpenFilePicker) {
          const handles = await window.showOpenFilePicker({
            multiple: false,
            types: [MD_PICKER]
          })
          const h = handles[0]
          if (!h) return { canceled: true }
          const path = await webVfs.registerOpenFile(h, webVfs.virtualPathForPickedFile(h.name))
          const content = await h.getFile().then((f) => f.text())
          return { canceled: false, path, content }
        }
        const files = await pickFilesViaInput('.md,.markdown,.mdown,.mkd', false)
        if (!files?.[0]) return { canceled: true }
        const file = files[0]
        const path = webVfs.registerMemoryFile(webVfs.virtualPathForPickedFile(file.name), file)
        webVfs.setDefaultDirectory(dirnamePath(path))
        const content = await file.text()
        return { canceled: false, path, content }
      } catch (e) {
        if (e instanceof DOMException && e.name === 'AbortError') return { canceled: true }
        console.error('[MyReader] openFileDialog', e)
        const msg = e instanceof Error ? e.message : String(e)
        window.alert(`打开文件失败：${msg}`)
        return { canceled: true }
      }
    },

    /** Web 版已关闭侧栏文件夹浏览；保留接口以兼容类型。 */
    openFolderDialog: async (): Promise<OpenFolderResult> => {
      return { canceled: true }
    },

    openImageDialog: async (): Promise<OpenImageResult> => {
      try {
        if (supportsFileSystemAccess() && window.showOpenFilePicker) {
          const handles = await window.showOpenFilePicker({
            multiple: false,
            types: [IMAGE_PICKER]
          })
          const h = handles[0]
          if (!h) return { canceled: true }
          const vPath = webVfs.virtualPathForPickedFile(h.name)
          await webVfs.registerOpenFile(h, vPath)
          const file = await h.getFile()
          const url = URL.createObjectURL(file)
          return { canceled: false, url, path: vPath }
        }
        const files = await pickFilesViaInput('image/*', false)
        if (!files?.[0]) return { canceled: true }
        const file = files[0]
        const vPath = webVfs.registerMemoryFile(webVfs.virtualPathForPickedFile(file.name), file)
        const url = URL.createObjectURL(file)
        return { canceled: false, url, path: vPath }
      } catch (e) {
        if (e instanceof DOMException && e.name === 'AbortError') return { canceled: true }
        console.error('[MyReader] openImageDialog', e)
        const msg = e instanceof Error ? e.message : String(e)
        window.alert(`选择图片失败：${msg}`)
        return { canceled: true }
      }
    },

    saveFileDialog: async (defaultPath?: string): Promise<SaveDialogResult> => {
      const name = defaultPath ? defaultPath.split(/[/\\]/).pop() : '未命名.md'
      try {
        const h = await webVfs.pickSaveHandle(name || '未命名.md')
        if (!h) return { canceled: true }
        let absPath = defaultPath ? normalizeAbsPath(defaultPath) : null
        if (isAbsDiskPath(absPath || '')) {
          /* keep disk path */
        } else if (absPath?.startsWith('/__web__/')) {
          absPath = webVfs.virtualPathForPickedFile(h.name)
        } else if (!absPath) {
          absPath = webVfs.virtualPathForPickedFile(h.name)
        } else {
          absPath = await promptFileAbsPath(h.name, defaultPath)
          if (!absPath) return { canceled: true }
        }
        const path = await webVfs.registerOpenFile(h, absPath!)
        return { canceled: false, path }
      } catch (e) {
        console.error('[MyReader] saveFileDialog', e)
        return { canceled: true }
      }
    },

    readFile: (filePath: string) => webVfs.readFile(filePath),

    writeFile: async (filePath: string, content: string) => {
      if (webVfs.hasPath(filePath)) {
        await webVfs.writeFile(filePath, content)
        return
      }
      const h = await webVfs.pickSaveHandle(baseNameFromPath(filePath))
      if (!h) throw new Error('保存已取消')
      const norm = normalizeAbsPath(filePath)
      let absPath: string | null = null
      if (isAbsDiskPath(norm)) {
        absPath = await promptFileAbsPath(h.name, filePath)
      } else if (norm.startsWith('/__web__/')) {
        absPath = webVfs.virtualPathForPickedFile(h.name)
      } else {
        absPath = await promptFileAbsPath(h.name, filePath)
      }
      if (!absPath) throw new Error('保存已取消')
      const path = await webVfs.registerOpenFile(h, absPath)
      await webVfs.writeFile(path, content)
    },

    listDirectory: (dirPath: string): Promise<ListDirResult> => webVfs.listDirectory(dirPath),

    parentDirectory: (dirPath: string): Promise<string> =>
      Promise.resolve(webVfs.parentDirectory(dirPath)),

    resolveRelativePath: async (baseFilePath: string, href: string): Promise<string> => {
      let raw = href.trim()
      const hashIdx = raw.indexOf('#')
      if (hashIdx >= 0) raw = raw.slice(0, hashIdx)
      const queryIdx = raw.indexOf('?')
      if (queryIdx >= 0) raw = raw.slice(0, queryIdx)
      raw = raw.trim()
      if (!raw) return ''
      if (/^https?:\/\//i.test(raw) || /^mailto:/i.test(raw) || /^tel:/i.test(raw)) return raw
      if (/^(blob:|data:)/i.test(raw)) return raw
      try {
        raw = decodeURIComponent(raw)
      } catch {
        /* keep */
      }
      return resolvePathFromBaseFile(baseFilePath, raw)
    },

    openExternal: async (url: string) => {
      const u = url.trim()
      if (/^https?:\/\//i.test(u) || /^mailto:/i.test(u) || /^tel:/i.test(u)) {
        window.open(u, '_blank', 'noopener,noreferrer')
      }
    },

    defaultDirectory: async () => webVfs.getDefaultDirectory(),

    deleteFile: (filePath: string) => webVfs.deleteFile(filePath),

    renamePath: (oldPath: string, newName: string) => webVfs.renamePath(oldPath, newName),

    allowClose: () => {
      closeAllowed = true
    },

    onRequestClose: (handler: () => void) => {
      closeHandler = handler
      return () => {
        if (closeHandler === handler) closeHandler = null
      }
    },

    onMenuAction: (handler: (action: string) => void) => {
      menuHandlers.add(handler)
      return () => menuHandlers.delete(handler)
    }
  }
}

function baseNameFromPath(p: string): string {
  const n = p.replace(/\\/g, '/')
  const i = n.lastIndexOf('/')
  return i >= 0 ? n.slice(i + 1) : n
}

export function installBrowserApi(): void {
  window.myreader = createBrowserApi()
}
