import { Injectable } from '@angular/core';
import Dexie from 'dexie';
import {IMigration, Migration} from './Migration';
import {IMenuItem, MenuItem} from './MenuItem';
import {KeyValue, IKeyValue} from './KeyValue';
import {Codium, ICodium} from './Codium';
import {Database, IDatabase} from './Database';
import {DatabaseColumn, IDatabaseColumn} from './DatabaseColumn';
import {DatabaseEntry, IDatabaseEntry} from './DatabaseEntry';
import {FileGroup, IFileGroup} from './FileGroup';
import {Page, IPage} from './Page';
import {PageNode, IPageNode} from './PageNode';

@Injectable({
    providedIn: 'root'
})
export class DatabaseService extends Dexie {
    protected static registeredModels = [
        Migration, MenuItem, KeyValue, Codium, Database, DatabaseColumn, DatabaseEntry, FileGroup, Page, PageNode
    ];

    protected initialized = false;

    migrations!: Dexie.Table<IMigration, number>;
    menuItems!: Dexie.Table<IMenuItem, number>;
    keyValues!: Dexie.Table<IKeyValue, number>;
    codiums!: Dexie.Table<ICodium, number>;
    databases!: Dexie.Table<IDatabase, number>;
    databaseColumns!: Dexie.Table<IDatabaseColumn, number>;
    databaseEntries!: Dexie.Table<IDatabaseEntry, number>;
    fileGroups!: Dexie.Table<IFileGroup, number>;
    pages!: Dexie.Table<IPage, number>;
    pageNodes!: Dexie.Table<IPageNode, number>;

    constructor(
    ) {
        super('NodedLocalDatabase');
    }

    public async getKeyValue(key: string): Promise<KeyValue> {
        const matches = await this.keyValues.where({ key }).toArray();

        if ( matches.length > 0 ) {
            return matches[0] as KeyValue;
        }

        return new KeyValue(key, '', false);
    }

    public async createSchemata() {
        if ( this.initialized ) {
            return;
        }
        this.initialized = true;

        console.log('db', this);

        const staticClass = this.constructor as typeof DatabaseService;
        const schema: any = {};

        for ( const ModelClass of staticClass.registeredModels ) {
            ModelClass.dbService = this;
            schema[ModelClass.getTableName()] = ModelClass.getSchema();
        }

        await this.version(17).stores(schema);
        await this.open();

        this.migrations = this.table('migrations');
        this.migrations.mapToClass(Migration);

        this.menuItems = this.table('menuItems');
        this.menuItems.mapToClass(MenuItem);

        this.keyValues = this.table('keyValues');
        this.keyValues.mapToClass(KeyValue);

        this.codiums = this.table('codiums');
        this.codiums.mapToClass(Codium);

        this.databases = this.table('databases');
        this.databases.mapToClass(Database);

        this.databaseColumns = this.table('databaseColumns');
        this.databaseColumns.mapToClass(DatabaseColumn);

        this.databaseEntries = this.table('databaseEntries');
        this.databaseEntries.mapToClass(DatabaseEntry);

        this.fileGroups = this.table('fileGroups');
        this.fileGroups.mapToClass(FileGroup);

        this.pages = this.table('pages');
        this.pages.mapToClass(Page);

        this.pageNodes = this.table('pageNodes');
        this.pageNodes.mapToClass(PageNode);
    }

    /** Return the local database records that need to be synced up with the server. */
    public async getDirtyRecords() {
        const codiums = await this.codiums.where({ needsServerUpdate: 1 }).toArray() as Codium[];
        const databases = await this.databases.where({ needsServerUpdate: 1 }).toArray() as Database[];
        const databaseColumns = await this.databaseColumns.where({ needsServerUpdate: 1 }).toArray() as DatabaseColumn[];
        const databaseEntries = await this.databaseEntries.where({ needsServerUpdate: 1 }).toArray() as DatabaseEntry[];
        const fileGroups = await this.fileGroups.where({ needsServerUpdate: 1 }).toArray() as FileGroup[];
        const pages = await this.pages.where({ needsServerUpdate: 1 }).toArray() as Page[];
        const pageNodes = await this.pageNodes.where({ needsServerUpdate: 1 }).toArray() as PageNode[];

        return {
            codiums: codiums.map(x => x.getSaveRecord()),
            databases: databases.map(x => x.getSaveRecord()),
            databaseColumns: databaseColumns.map(x => x.getSaveRecord()),
            databaseEntries: databaseEntries.map(x => x.getSaveRecord()),
            fileGroups: fileGroups.map(x => x.getSaveRecord()),
            pages: pages.map(x => x.getSaveRecord()),
            pageNodes: pageNodes.map(x => x.getSaveRecord()),
        };
    }

    public async purge() {
        console.warn('Purging all local data!');

        await Promise.all([
            this.migrations.clear(),
            this.menuItems.clear(),
            this.keyValues.clear(),
            this.codiums.clear(),
            this.databases.clear(),
            this.databaseColumns.clear(),
            this.databaseEntries.clear(),
            this.fileGroups.clear(),
            this.pages.clear(),
            this.pageNodes.clear(),
        ]);
    }
}
