import { reactive, UnwrapNestedRefs, WritableComputedRef, computed } from 'vue'

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

        // 读取本地数据
        for (const key in this.state) {
            const storageValue = this.storage.getItem(this.prefix + key.toString())
            if (storageValue) {
                this.state[key] = JSON.parse(storageValue)
            }
        }
    }

    private readonly storage: Storage
    private readonly source // 初始数据
    private readonly prefix // 防止命名冲突
    private state

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

    /**
     * 更新数据
     * @param key 
     * @param value 
     */
    setValue<K extends keyof UnwrapNestedRefs<T>>(key: K, value: UnwrapNestedRefs<T>[K]) {
        if (value === undefined || value === null) {
            this.storage.removeItem(this.prefix + key.toString())
        } else {
            const strValue = JSON.stringify(value) // 注意数值长度过长会被自动转换为 String 类型
            this.storage.setItem(this.prefix + key.toString(), strValue)
        }
        this.state[key] = value
    }

    /**
     * 获取响应对象
     * @returns 
     */
    getRefs() {
        const refs: { [K in keyof UnwrapNestedRefs<T>]: WritableComputedRef<UnwrapNestedRefs<T>[K]> } = Object.create(null)
        for (const key in this.state) {
            refs[key] = this.getRef(key)
        }
        return refs
    }

    /**
     * 获取响应数据
     * @param key 
     * @returns 
     */
    getRef<K extends keyof Record<keyof UnwrapNestedRefs<T>, T>>(key: K) {
        return computed({
            get: () => this.getValue(key),
            set: (val) => this.setValue(key, val)
        })
    }

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