import {BehaviorSubject, distinctUntilChanged, map, Observable} from "rxjs";
import {Injectable} from "@angular/core";
import {LocalStorageRefService} from "./local-storage-ref.service";
import {Instance, State} from "./state";

const state = <State>{};

@Injectable({providedIn: 'root'})
export class PersistentStore {
    private _localStorage: Storage;
    private subject = new BehaviorSubject<State>(state);
    private store = this.subject.asObservable().pipe(distinctUntilChanged());

    constructor(private _localStorageRefService: LocalStorageRefService) {
        this._localStorage = _localStorageRefService.localStorage;
        this.loadState();
    }

    get value() {
        return this.subject.value;
    }

    isAuthorized(): boolean {
        const currentInstance = this.value.currentInstance;
        if (!currentInstance) return false;
        if (!currentInstance.accessToken) return false;
        if (!currentInstance.accountId) return false;
        return true;
    }

    select<T>(name: string): Observable<T> {
        return this.store.pipe(map(value => value ? <T>value[name as keyof State] : <T>{}));
    }

    set(name: string, state: any) {
        this.subject.next({
            ...this.value, [name as keyof State]: state
        });
        this.persistState();
    }

    private persistState() {
        const jsonData = JSON.stringify(this.subject.value, this.stringifyMap);
        this._localStorage.setItem('applicationData', jsonData);
    }

    private loadState() {
        const jsonData = this._localStorage.getItem('applicationData');
        if (jsonData) {
            const data = JSON.parse(jsonData, this.parseMap);
            this.subject.next(data);
        }
    }


    private stringifyMap(key: string, value: Map<string, Instance> | Map<string, string> | object) {
        if (value instanceof Map) {
            return {
                dataType: 'Map',
                value: [...value]
            };
        } else {
            return value;
        }
    }

    private parseMap(key: string, value: any) {
        if (typeof value === 'object' && value !== null) {
            if (value.dataType === 'Map') {
                return new Map(value.value);
            }
        }
        return value;
    }
}
