import { create } from 'zustand'
import type { AppSettings, LoadPayload, Page, ProjectJson, ScreenInfo, ViewMode } from './types'
import { defaultPages, jsonToPages, nextPageId, pageSrc, pagesToJson } from './utils'
import { toast } from './toast'
import { askPassword } from './passwordState'

interface AppState {
  pages: Page[]
  curIdx: number
  view: ViewMode
  filePath: string | null
  dirty: boolean
  screens: ScreenInfo[]
  zoom: number
  settings: AppSettings | null
  // actions
  setView: (v: ViewMode) => void
  selectPage: (i: number) => void
  updatePage: (patch: Partial<Page>) => void
  addPage: () => void
  insertBefore: (index?: number) => void
  dupPage: () => void
  movePage: (dir: number) => void
  delPage: () => void
  capturePreview: (index?: number) => Promise<void>
  resetOrder: () => void
  setScreens: (s: ScreenInfo[]) => void
  setZoom: (z: number) => void
  loadSettings: () => Promise<AppSettings>
  updateSettings: (patch: Partial<AppSettings>) => Promise<AppSettings>
  loadProject: (payload: LoadPayload) => 'edit' | 'publish'
  save: () => Promise<void>
  saveAs: () => Promise<void>
  open: () => Promise<void>
  markSaved: () => void
}

/** 应用工程 JSON 到编辑状态 */
function applyProject(get: () => AppState, set: (p: Partial<AppState>) => void, path: string, json: ProjectJson): void {
  const pages = jsonToPages(json)
  set({ pages, curIdx: 0, filePath: path, dirty: false })
  try {
    window.api.syncProject(pages.map((p) => ({ ctrl: p.ctrl, title: p.title, url: pageSrc(p) })))
  } catch {
    /* 无碍 */
  }
  toast('已打开工程：' + path.split(/[\\/]/).pop(), 'ok')
}

export const useStore = create<AppState>((set, get) => {
  const touch = (patch: Partial<AppState>): void => set({ ...patch, dirty: true })

  return {
    pages: defaultPages(),
    curIdx: 0,
    view: 'edit',
    filePath: null,
    dirty: false,
    screens: [],
    zoom: 100,
    settings: null,

    setView: (v) => set({ view: v }),
    selectPage: (i) => {
      const pages = get().pages
      const idx = Math.max(0, Math.min(i, pages.length - 1))
      set({ curIdx: idx })
    },
    updatePage: (patch) => {
      const { pages, curIdx } = get()
      const list = pages.map((p, i) => (i === curIdx ? { ...p, ...patch } : p))
      touch({ pages: list })
    },
    addPage: () => {
      const pages = get().pages
      const id = nextPageId(pages)
      const page: Page = {
        id,
        title: '新页面 ' + pages.length + 1,
        desc: '新建页面',
        screen: 1,
        ctrl: 0,
        display: true,
        url: 'sample-screen.html',
        params: `no=${id.replace(/\D/g, '')}\ntitle=${id}`
      }
      const list = [...pages, page]
      touch({ pages: list, curIdx: list.length - 1 })
      toast('已新建页面 ' + id, 'ok')
    },
    insertBefore: (index?: number) => {
      const { pages, curIdx } = get()
      const at = index ?? curIdx
      const id = nextPageId(pages)
      const page: Page = {
        id,
        title: '新页面 ' + pages.length + 1,
        desc: '新建页面',
        screen: 1,
        ctrl: 0,
        display: true,
        url: 'sample-screen.html',
        params: `no=${id.replace(/\D/g, '')}\ntitle=${id}`
      }
      const list = [...pages]
      list.splice(at, 0, page)
      touch({ pages: list, curIdx: at })
      toast('已在页面前插入 ' + id, 'ok')
    },
    dupPage: () => {
      const { pages, curIdx } = get()
      const src = pages[curIdx]
      if (!src) return
      const copy: Page = {
        ...src,
        id: nextPageId(pages),
        title: src.title + '（副本）'
      }
      const list = [...pages]
      list.splice(curIdx + 1, 0, copy)
      touch({ pages: list, curIdx: curIdx + 1 })
      toast('已复制页面 ' + copy.id, 'ok')
    },
    movePage: (dir) => {
      const { pages, curIdx } = get()
      const ni = curIdx + dir
      if (ni < 0 || ni >= pages.length) {
        toast('已在边界，无法移动', 'warn')
        return
      }
      const list = [...pages]
      ;[list[curIdx], list[ni]] = [list[ni], list[curIdx]]
      touch({ pages: list, curIdx: ni })
    },
    delPage: () => {
      const { pages, curIdx } = get()
      if (pages.length <= 1) {
        toast('至少保留一个页面', 'warn')
        return
      }
      const rm = pages[curIdx]
      const list = pages.filter((_, i) => i !== curIdx)
      touch({ pages: list, curIdx: Math.min(curIdx, list.length - 1) })
      toast('已删除页面 ' + rm.id, 'ok')
    },
    capturePreview: async (index?: number) => {
      const { pages } = get()
      const at = index ?? get().curIdx
      const p = pages[at]
      if (!p) return
      const src = pageSrc(p)
      if (src === 'about:blank') {
        toast('空白页无法截图，请先设置 URL', 'warn')
        return
      }
      toast('正在截图…', 'info')
      try {
        const data = await window.api.page.snapshot(src)
        if (data) {
          const list = pages.map((x, i) => (i === at ? { ...x, preview: data } : x))
          touch({ pages: list })
          toast('页面截图已更新', 'ok')
        } else {
          toast('截图失败，请确认 URL 可访问', 'warn')
        }
      } catch (err) {
        toast('截图失败：' + String(err), 'warn')
      }
    },
    resetOrder: () => {
      const pages = get().pages
      const list = pages.map((p, i) => ({
        ...p,
        id: 'P-' + String(i + 1).padStart(3, '0'),
        screen: i + 1,
        ctrl: i + 1
      }))
      touch({ pages: list })
      toast('页面已重排：屏幕 1-' + list.length + '、快捷键 1-' + list.length + '、PageID 已重排', 'ok')
    },
    setScreens: (s) => set({ screens: s }),
    setZoom: (z) => set({ zoom: Math.min(160, Math.max(50, z)) }),

    loadSettings: async () => {
      const s = await window.api.settings.get()
      set({ settings: s })
      return s
    },
    updateSettings: async (patch) => {
      const s = await window.api.settings.set(patch)
      set({ settings: s })
      return s
    },
    loadProject: (payload) => {
      if (payload?.json) applyProject(get, set, payload.path, payload.json)
      return payload?.mode ?? 'edit'
    },

    save: async () => {
      const { filePath, pages, settings } = get()
      const meta = { appName: settings?.projectName, version: settings?.projectVersion }
      const data = JSON.stringify(pagesToJson(pages, meta), null, 2)
      const pwd = settings?.jsonPassword || undefined
      try {
        if (filePath) {
          const r = await window.api.project.save(filePath, data, pwd)
          if (r) {
            set({ dirty: false })
            toast('工程已保存', 'ok')
          }
        } else {
          const path = await window.api.project.saveAs(data, pwd)
          if (path) {
            set({ filePath: path, dirty: false })
            toast('工程已保存为 ' + path.split(/[\\/]/).pop(), 'ok')
          }
        }
      } catch (err) {
        toast('保存失败：' + String(err), 'warn')
      }
    },
    saveAs: async () => {
      const { pages, settings } = get()
      const meta = { appName: settings?.projectName, version: settings?.projectVersion }
      const data = JSON.stringify(pagesToJson(pages, meta), null, 2)
      const pwd = settings?.jsonPassword || undefined
      try {
        const path = await window.api.project.saveAs(data, pwd)
        if (path) {
          set({ filePath: path, dirty: false })
          toast('工程已另存为', 'ok')
        }
      } catch (err) {
        toast('保存失败：' + String(err), 'warn')
      }
    },
    open: async () => {
      try {
        const settings = get().settings ?? (await get().loadSettings())
        let r = await window.api.project.open(settings.jsonPassword || undefined)
        if (!r) return
        if (r.json) {
          applyProject(get, set, r.path, r.json)
          return
        }
        if (r.error === '密码错误') {
          const pwd = await askPassword('打开工程需要密码')
          if (!pwd) return
          r = await window.api.project.openAt(r.path, pwd)
          if (r?.json) {
            applyProject(get, set, r.path, r.json)
            // 密码正确后记忆到设置，后续打开免输入
            void get().updateSettings({ jsonPassword: pwd })
          } else {
            toast('密码错误，无法解密该工程文件', 'warn')
          }
          return
        }
        toast(r.error || '打开失败', 'warn')
      } catch (err) {
        toast('打开失败：' + String(err), 'warn')
      }
    },
    markSaved: () => set({ dirty: false })
  }
})
