/*
Copyright 2020 The Matrix.org Foundation C.I.C.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import EventEmitter from "events";
import { base32 } from "rfc4648";
import { RoomType } from "matrix-js-sdk/src/@types/event";

// Dev note: the interface is split in two so we don't have to disable the
// linter across the whole project.
export interface IThreepidInviteWireFormat {
    email: string;
    signurl: string;
    room_name: string; // eslint-disable-line camelcase
    room_avatar_url: string; // eslint-disable-line camelcase
    inviter_name: string; // eslint-disable-line camelcase

    // TODO: Figure out if these are ever populated
    guest_access_token?: string; // eslint-disable-line camelcase
    guest_user_id?: string; // eslint-disable-line camelcase
}

interface IPersistedThreepidInvite extends IThreepidInviteWireFormat {
    roomId: string;
}

export interface IThreepidInvite {
    id: string; // generated by us
    roomId: string;
    toEmail: string;
    signUrl: string;
    roomName: string;
    roomAvatarUrl: string;
    inviterName: string;
}

// Any data about the room that would normally come from the homeserver
// but has been passed out-of-band, eg. the room name and avatar URL
// from an email invite (a workaround for the fact that we can't
// get this information from the HS using an email invite).
export interface IOOBData {
    name?: string; // The room's name
    avatarUrl?: string; // The mxc:// avatar URL for the room
    inviterName?: string; // The display name of the person who invited us to the room
    // eslint-disable-next-line camelcase
    room_name?: string; // The name of the room, to be used until we are told better by the server
    roomType?: RoomType; // The type of the room, to be used until we are told better by the server
}

const STORAGE_PREFIX = "mx_threepid_invite_";

export default class ThreepidInviteStore extends EventEmitter {
    private static _instance: ThreepidInviteStore;

    public static get instance(): ThreepidInviteStore {
        if (!ThreepidInviteStore._instance) {
            ThreepidInviteStore._instance = new ThreepidInviteStore();
        }
        return ThreepidInviteStore._instance;
    }

    public storeInvite(roomId: string, wireInvite: IThreepidInviteWireFormat): IThreepidInvite {
        const invite = <IPersistedThreepidInvite>{ roomId, ...wireInvite };
        const id = this.generateIdOf(invite);
        localStorage.setItem(`${STORAGE_PREFIX}${id}`, JSON.stringify(invite));
        return this.translateInvite(invite);
    }

    public getWireInvites(): IPersistedThreepidInvite[] {
        const results: IPersistedThreepidInvite[] = [];
        for (let i = 0; i < localStorage.length; i++) {
            const keyName = localStorage.key(i);
            if (!keyName.startsWith(STORAGE_PREFIX)) continue;
            results.push(JSON.parse(localStorage.getItem(keyName)) as IPersistedThreepidInvite);
        }
        return results;
    }

    public getInvites(): IThreepidInvite[] {
        return this.getWireInvites().map(i => this.translateInvite(i));
    }

    // Currently Element can only handle one invite at a time, so handle that
    public pickBestInvite(): IThreepidInvite {
        return this.getInvites()[0];
    }

    public resolveInvite(invite: IThreepidInvite) {
        localStorage.removeItem(`${STORAGE_PREFIX}${invite.id}`);
    }

    private generateIdOf(persisted: IPersistedThreepidInvite): string {
        // Use a consistent "hash" to form an ID.
        return base32.stringify(Buffer.from(JSON.stringify(persisted)));
    }

    private translateInvite(persisted: IPersistedThreepidInvite): IThreepidInvite {
        return {
            id: this.generateIdOf(persisted),
            roomId: persisted.roomId,
            toEmail: persisted.email,
            signUrl: persisted.signurl,
            roomName: persisted.room_name,
            roomAvatarUrl: persisted.room_avatar_url,
            inviterName: persisted.inviter_name,
        };
    }

    public translateToWireFormat(invite: IThreepidInvite): IThreepidInviteWireFormat {
        return {
            email: invite.toEmail,
            signurl: invite.signUrl,
            room_name: invite.roomName,
            room_avatar_url: invite.roomAvatarUrl,
            inviter_name: invite.inviterName,
        };
    }
}
