import { Storage } from '@ionic/storage';
import { Storable } from './storable';

export class StorageWrapper<T extends Storable> {
    entries: Array<T> = [];
    activeEntries: Array<T> = [];
    storageKey: string;
    lastMigration = 1;

    createdIds: Set<string> = new Set();
    updatedIds: Set<string> = new Set();
    deletedIds: Set<string> = new Set();

    constructor(storageKey: string, private storageType: any, private storage: Storage, private seedData?: T[]) {
        this.storageKey = storageKey;
    }

    private async deserialize_entries() {
        const parsedEntries = await this.storage.get(this.storageKey);
        const journalEntries = parsedEntries.map(entry => new this.storageType(entry));
        return journalEntries;
    }

    private async serialize_entries() {
        const plainObjectEntries = this.entries.map(entry => entry.convertToPlainObject());
        return this.storage.set(this.storageKey, plainObjectEntries);
    }

    async getEntries(force = false) {
        if (this.entries.length === 0 || force) {
            const entriesInStorage = await this.storage.get(this.storageKey);

            if (!entriesInStorage) {
                this.entries = this.seedData;
                await this.serialize_entries();
            } else {
                this.entries = await this.deserialize_entries();
            }

            this.activeEntries = this.entries.filter(entry => !entry.archived);
        }

        return this.activeEntries;
    }

    async addEntry(newEntry: T) {
        // Make sure the old entries have been fetched before adding new ones
        await this.getEntries();

        this.entries.push(newEntry);
        this.activeEntries.push(newEntry);

        this.createdIds.add(newEntry.id);
        this.serialize_entries();
    }

    async getEntryById(id: string) {
        const journalEntries = await this.getEntries();
        return journalEntries.find(entry => entry.id === id);
    }

    async updateEntry(updateObject: Storable) {
        const entryToUpdate = this.entries.find(entry => entry.id === updateObject.id);
        entryToUpdate.update(updateObject);
        const newActiveEntries = this.entries.filter(entry => !entry.archived);
        this.activeEntries.splice(0, Infinity, ...newActiveEntries);

        this.updatedIds.add(updateObject.id);
        this.serialize_entries();
    }

    async softDeleteEntry(deletedId: string) {
        this.entries.find(entry => entry.id === deletedId).archived = true;
        const newActiveEntries = this.entries.filter(entry => !entry.archived);
        this.activeEntries.splice(0, Infinity, ...newActiveEntries);

        this.updatedIds.add(deletedId);
        this.serialize_entries();
    }

    async hardDeleteEntry(deletedId: string) {
        const deletedEntryIndex = this.entries.findIndex(entry => entry.id === deletedId);
        this.entries.splice(deletedEntryIndex, 1);
        const deletedActiveEntryIndex = this.activeEntries.findIndex(entry => entry.id === deletedId);
        this.activeEntries.splice(deletedActiveEntryIndex, Infinity);

        this.deletedIds.add(deletedId);
        this.serialize_entries();
    }
}
