import type localforage from 'localforage'
import { i18n } from '@/i18n'

export interface DataEntry {
  key: string
  value: any
}

export interface ForageStoreSnapshot {
  storeName: string
  entries: DataEntry[]
}

export interface DataPackage {
  version: number
  packageType?: 'project-snapshot'
  exportedAt: string
  projectId?: string
  localStorage: DataEntry[]
  sessionStorage: DataEntry[]
  localforageDefault: DataEntry[]
  localforageStores?: ForageStoreSnapshot[]
}

export type ForageInstance = ReturnType<typeof localforage.createInstance>
export type ForageStore = Pick<ForageInstance, 'keys' | 'getItem' | 'setItem' | 'clear'>

type XmInfoLike = {
  projectName?: unknown
}

export const readWebStorage = (storageObj: Storage): DataEntry[] => {
  const entries: DataEntry[] = []
  for (let i = 0; i < storageObj.length; i++) {
    const key = storageObj.key(i)
    if (!key) continue
    const raw = storageObj.getItem(key)
    let value: any = raw
    if (raw != null) {
      try {
        value = JSON.parse(raw)
      } catch {
        value = raw
      }
    }
    entries.push({ key, value })
  }
  return entries
}

export const writeWebStorage = (storageObj: Storage, entries: DataEntry[]) => {
  storageObj.clear()
  for (const entry of entries || []) {
    const value = typeof entry.value === 'string' ? entry.value : JSON.stringify(entry.value)
    storageObj.setItem(entry.key, value)
  }
}

export const toPersistableValue = (value: unknown) => {
  try {
    return JSON.parse(JSON.stringify(value))
  } catch (error) {
    console.error('normalize persist value failed, fallback to null:', error)
    return null
  }
}

export const readForage = async (store: ForageStore): Promise<DataEntry[]> => {
  const keys = await store.keys()
  const values = await Promise.all(keys.map(key => store.getItem(key)))
  return keys.map((key, index) => ({
    key,
    value: toPersistableValue(values[index])
  }))
}

export const writeForage = async (store: ForageStore, entries: DataEntry[]) => {
  await store.clear()
  await Promise.all((entries || []).map(entry => store.setItem(entry.key, toPersistableValue(entry.value))))
}

export const normalizeEntries = (value: unknown): DataEntry[] => {
  if (!Array.isArray(value)) return []
  return value
    .filter(item => item && typeof item === 'object' && typeof (item as any).key === 'string')
    .map(item => ({ key: String((item as any).key), value: (item as any).value }))
}

export const normalizeForageStoreSnapshots = (value: unknown): ForageStoreSnapshot[] => {
  if (!Array.isArray(value)) return []
  return value
    .filter(item =>
      item
      && typeof item === 'object'
      && typeof (item as any).storeName === 'string'
      && Array.isArray((item as any).entries)
    )
    .map(item => ({
      storeName: String((item as any).storeName),
      entries: normalizeEntries((item as any).entries)
    }))
}

export const sanitizeFileNamePart = (value: string): string => {
  const cleaned = value
    .replace(/[\\/:*?"<>|]/g, '_')
    .replace(/\s+/g, ' ')
    .trim()
  return cleaned || i18n.global.t('tab.messages.defaultProjectName')
}

export const getExportProjectName = (entries: DataEntry[], projectInfoDbKey: string, legacyProjectDbKey: string) => {
  const target =
    entries.find(item => item.key === projectInfoDbKey) ||
    entries.find(item => item.key === legacyProjectDbKey)
  const data = (target?.value || {}) as XmInfoLike
  return typeof data.projectName === 'string'
    ? sanitizeFileNamePart(data.projectName)
    : i18n.global.t('tab.messages.defaultProjectName')
}

export const isDataPackageLike = (value: unknown): value is DataPackage => {
  if (!value || typeof value !== 'object') return false
  const payload = value as Partial<DataPackage>
  const hasRequiredArrays =
    Array.isArray(payload.localStorage) &&
    Array.isArray(payload.sessionStorage) &&
    Array.isArray(payload.localforageDefault)
  if (!hasRequiredArrays) return false
  if (typeof payload.version !== 'number' || !Number.isFinite(payload.version)) return false
  if (payload.packageType != null && payload.packageType !== 'project-snapshot') return false
  if (payload.projectId != null && typeof payload.projectId !== 'string') return false
  return true
}
