import { shallowRef, ShallowRef, watch } from 'vue'

/**
 * 本地存储类
 */
export default class <T extends object> {
    constructor(storage: Storage, source: T) {
        this.storage = storage
        this.source = source

        // 读取本地数据
        for (const key in source) {
            const storageValue = this.getValue(key)
            this.state[key] = shallowRef(storageValue)
            this.observe(key)
        }
    }

    private readonly storage: Storage
    private readonly source // 初始数据
    private state = {} as { [K in keyof T]: ShallowRef<T[K]> }

    /**
     * 监听数据变化
     * @param key 
     */
    private observe<K extends keyof T>(key: K) {
        watch(this.getRef(key), (value) => {
            if (value === undefined || value === null) {
                this.reset(key)
            } else {
                const strValue = JSON.stringify(value) // 注意数值长度过长会被自动转换为 String 类型
                this.storage.setItem(key.toString(), strValue)
            }
        })
    }

    /**
     * 获取数据
     * @param key 
     * @returns 
     */
    getValue<K extends keyof T>(key: K) {
        const storageValue = this.storage.getItem(key.toString())
        if (storageValue !== 'undefined' && storageValue !== null) {
            return JSON.parse(storageValue) as T[K]
        } else {
            return this.source[key]
        }
    }

    /**
     * 更新数据
     * @param key 
     * @param value 
     */
    setValue<K extends keyof T>(key: K, value: T[K]) {
        const state = this.getRef(key)
        state.value = value
    }

    /**
     * 获取响应对象
     * @returns 
     */
    getRefs() {
        return this.state
    }

    /**
     * 获取响应数据
     * @param key 
     * @returns 
     */
    getRef<K extends keyof T>(key: K) {
        return this.state[key]
    }

    /**
     * 重置数据
     * @param keys 
     */
    reset<K extends keyof T>(...keys: K[]) {
        if (keys.length) {
            keys.forEach((key) => {
                const state = this.getRef(key)
                state.value = this.source[key]
            })
        } else {
            for (const key in this.state) {
                this.state[key].value = this.source[key]
            }
        }
    }
}