import { MessageInterface, MessageListener } from '../communication-interface/MessageInterface';
import { getRandomUUID } from '../crypto';
import { ObjectStore } from '../db/DBAccess';
import { DBFactory } from '../db/DBFactory';
import { MessageDto } from '../libDto/MessageDto';


export class DeliveryGuarantee implements MessageInterface {
    private _initPromise: Promise<void>;
    messageStore?: ObjectStore<MessageDto>;

    constructor(private messagetransport: MessageInterface, dbFactory: DBFactory) {
        this.messagetransport = messagetransport;
        this._initPromise = (async () => {
            this.messageStore = await dbFactory.getDao(MessageDto);
        })();
        this.messagetransport.addMessageListener(this._messageRecieved);
        setInterval(this._sendUnackedMessages.bind(this), 20000);
    }

    /**
     * This function retransmits all unacked messages older than 60 seconds.
     */
    private async _sendUnackedMessages(): Promise<void> {
        await this._initPromise;
        const unackedMessages = await this.messageStore!.getAll();
        await Promise.all(unackedMessages.map(async (message) => {
            if (message.timestamp.getTime() < new Date().getTime() - 365 * 24 * 60 * 60 * 1000) {
                await this.messageStore!.delete(message);
            }
            if (message.timestamp.getTime() + 60000 < new Date().getTime()) {
                await this.send(message.to, JSON.stringify({ type: 'msg', id: message.id, payload: message.payload }));
            }
        }));
    }

    async send(userId: string, payload: string): Promise<void> {
        const message = new MessageDto(await getRandomUUID(), userId, payload);
        payload = JSON.stringify({ type: 'msg', id: message.id, payload });
        await this.messagetransport.send(userId, payload);
    }

    listeners: MessageListener[] = [];

    addMessageListener(listener: MessageListener): void {
        this.listeners.push(listener);
    }

    async _messageRecieved(fromId: string, payload: string): Promise<void> {
        const parsedMessage = JSON.parse(payload);
        if (parsedMessage.type === 'msg') {
            await this.send(fromId, JSON.stringify({ type: 'ack', id: parsedMessage.id }));
            this.listeners.forEach(cb => {
                cb(fromId, parsedMessage.payload);
            });
        } else if (parsedMessage.type === 'ack') {
            await this._initPromise;
            await this.messageStore!.delete(parsedMessage.id);
        }
    }

    addContact(userId: string, keyInformation: any): Promise<string | void> {
        return this.messagetransport.addContact(userId, keyInformation);
    }

    getKeyInformation(): Promise<any> {
        return this.messagetransport.getKeyInformation();
    }

    getOwnUserId(): Promise<string> {
        return this.messagetransport.getOwnUserId();
    }


}