import { IDBPDatabase } from 'idb';
import { BaseDto, DtoClass, ObjectStore } from './DBAccess';
import { TransactionManager } from './Transaction';
import { PushTransportDto } from '../libDto/PushTransportDto';
import { SignalProtocolDto } from '../libDto/SignalProtocolDto';
import { ContactDto } from '../libDto/ContactDto';
import { ContactAddressDto } from '../libDto/ContactAddressDto';
import { NonceDto } from '../libDto/NonceDto';
import { EcdheAesGcmContactDto } from '../crypto/ecdhe-aes-gcm-message';
import { EcdheAesGcmKeyDto } from '../dht-network/DhtTransportManager';

export interface ObjectStoreInitializer {
    objectStore: DtoClass;
    objectStoreName: string;
}

export class DBFactory {
    transactionManager = new TransactionManager();
    private stores: Map<string, ObjectStore<BaseDto>> = new Map();
    initPromise: Promise<void>;
    private dtoClasses: DtoClass[] = [];

    getDtoClasses(): DtoClass[] {
        return this.dtoClasses;
    }

    constructor(classes: DtoClass[], dbName = 'PrivDB') {
        this.dtoClasses = classes;
        this.initPromise = (async () => {
            for (const objectClass of classes) {
                this.transactionManager.addObjectStore(objectClass.dbName);
            }
            await this.transactionManager.init(dbName);
            if (!this.transactionManager.db) {
                throw new Error('TransactionManager init failed');
            }
            for (const objectClass of classes) {
                //new initializer.objectStore(initializer.objectStoreName, this.transactionManager.db)
                this.stores.set(objectClass.dbName, new ObjectStore<BaseDto>(objectClass.dbName, this.transactionManager));
            }
        })();
    }

    async getDao<T extends ObjectStore<BaseDto>>(objectClass: DtoClass): Promise<T> {
        await this.initPromise;
        if (!this.stores.has(objectClass.dbName)) {
            throw new Error(`cannot find ${objectClass.dbName}! Did you add it in the createFactory method?`);
        }
        return this.stores.get(objectClass.dbName) as T;
    }

    async getObjectById(id: string): Promise<BaseDto | undefined> {
        await this.initPromise;
        const objectStore = this.stores.get(id.split(':')[0]);
        if (!objectStore) {
            throw new Error('ObjectStore not found');
        }
        return objectStore.get(id);
    }
}

let dbFactory: DBFactory | undefined;

export function createFactory(classes: DtoClass[]): void {
    classes.push(...[PushTransportDto, SignalProtocolDto, ContactDto, ContactAddressDto, NonceDto, EcdheAesGcmContactDto, EcdheAesGcmKeyDto]);
    if (!dbFactory) {
        dbFactory = new DBFactory(classes);
    }
    else {
        console.warn('Factory already instantiated');
    }
}

export function getFactory(): DBFactory {
    if (!dbFactory) {
        throw new Error('Factory not instantiated');
    }
    return dbFactory;
}