import { LOCAL_STORAGE_KEYS, SESSION_STORAGE_KEYS } from '@/constants/storage'
import dayjs from 'dayjs'

interface StorageData {
  cache?: string
  data: any
}

export type StorageType = 'local' | 'session'
type LOCAL_KEY = keyof typeof LOCAL_STORAGE_KEYS
type SESSION_KEY = keyof typeof SESSION_STORAGE_KEYS
type StorageKey<T> = T extends 'local' ? LOCAL_KEY : SESSION_KEY

class StorageTool<T extends StorageType = 'local'> {
  storage: Storage
  keyMap: Record<string, string>
  storageType: StorageType
  constructor(storageType: T) {
    this.storageType = storageType || 'local'
    this.storage = this.storageType === 'local' ? window.localStorage : window.sessionStorage
    this.keyMap = this.storageType === 'local' ? LOCAL_STORAGE_KEYS : SESSION_STORAGE_KEYS
  }

  get<D = any>(storageKey: StorageKey<T>): D | null {
    const key = this.keyMap[storageKey]
    const value = this.storage.getItem(key)
    try {
      const data: StorageData | null = typeof value === 'string' ? (JSON.parse(value) as StorageData) : value
      if (data !== null) {
        if (data.cache) {
          if (dayjs(new Date(data.cache)).isAfter(Date.now())) {
            return data.data
          }
          this.remove(storageKey)
          return null
        }
        return data.data
      }
      return data
    } catch (error) {
      console.error(error)
      return null
    }
  }

  set(storageKey: StorageKey<T>, data: unknown, option?: { expires: string | Date }) {
    const key = this.keyMap[storageKey]
    let cache: string | Date = ''
    if (option?.expires) {
      try {
        cache = new Date(option.expires)
      } catch (error) {
        console.warn(`set storage ${key} expires error`)
      }
    }
    return this.storage.setItem(key, JSON.stringify({ data, cache }))
  }

  remove(storageKey: StorageKey<T>) {
    const key = this.keyMap[storageKey]
    return this.storage.removeItem(key)
  }

  clear() {
    return this.storage.clear()
  }
}

export const sessionStorage = new StorageTool('session')

export const localStorage = new StorageTool('local')
