// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import {Client4} from 'mattermost-redux/client';
import {ClientError} from 'mattermost-redux/client/client4';

import manifest from 'manifest';
import {AllBadgesBadge, Badge, BadgeDetails, BadgeID, BadgeTypeDefinition, CreateBadgeRequest, CreateTypeRequest, GetTypesResponse, GrantBadgeRequest, RevokeOwnershipRequest, SubscriptionRequest, UpdateBadgeRequest, UpdateTypeRequest, UserBadge} from 'types/badges';

export default class Client {
    private url: string;

    constructor() {
        this.url = '/plugins/' + manifest.id + '/api/v1';
    }

    async getUserBadges(userID: string): Promise<UserBadge[]> {
        try {
            const res = await this.doGet(`${this.url}/getUserBadges/${userID}`);
            return res as UserBadge[];
        } catch {
            return [];
        }
    }

    async getBadgeDetails(badgeID: BadgeID): Promise<BadgeDetails|null> {
        try {
            const res = await this.doGet(`${this.url}/getBadgeDetails/${badgeID}`);
            return res as BadgeDetails;
        } catch {
            return null;
        }
    }

    async getAllBadges(): Promise<AllBadgesBadge[]> {
        try {
            const res = await this.doGet(`${this.url}/getAllBadges`);
            return res as AllBadgesBadge[];
        } catch {
            return [];
        }
    }

    async getTypes(): Promise<GetTypesResponse> {
        try {
            const res = await this.doGet(`${this.url}/getTypes`);
            return res as GetTypesResponse;
        } catch {
            return {types: [], can_create_type: false, can_edit_type: false};
        }
    }

    async createBadge(req: CreateBadgeRequest): Promise<Badge> {
        return await this.doPost(`${this.url}/createBadge`, req) as Badge;
    }

    async createType(req: CreateTypeRequest): Promise<BadgeTypeDefinition> {
        return await this.doPost(`${this.url}/createType`, req) as BadgeTypeDefinition;
    }

    async updateBadge(req: UpdateBadgeRequest): Promise<Badge> {
        return await this.doPut(`${this.url}/updateBadge`, req) as Badge;
    }

    async deleteBadge(badgeID: BadgeID): Promise<void> {
        await this.doDelete(`${this.url}/deleteBadge/${badgeID}`);
    }

    async updateType(req: UpdateTypeRequest): Promise<BadgeTypeDefinition> {
        return await this.doPut(`${this.url}/updateType`, req) as BadgeTypeDefinition;
    }

    async deleteType(typeID: string): Promise<void> {
        await this.doDelete(`${this.url}/deleteType/${typeID}`);
    }

    async grantBadge(req: GrantBadgeRequest): Promise<void> {
        await this.doPost(`${this.url}/grantBadge`, req);
    }

    async createSubscription(req: SubscriptionRequest): Promise<void> {
        await this.doPost(`${this.url}/createSubscription`, req);
    }

    async deleteSubscription(req: SubscriptionRequest): Promise<void> {
        await this.doPost(`${this.url}/deleteSubscription`, req);
    }

    async revokeOwnership(req: RevokeOwnershipRequest): Promise<void> {
        await this.doPost(`${this.url}/revokeOwnership`, req);
    }

    async getChannelSubscriptions(channelID: string): Promise<BadgeTypeDefinition[]> {
        try {
            const res = await this.doGet(`${this.url}/getChannelSubscriptions/${channelID}`);
            return res as BadgeTypeDefinition[];
        } catch {
            return [];
        }
    }

    private doGet = async (url: string, headers: {[x:string]: string} = {}) => {
        headers['X-Timezone-Offset'] = String(new Date().getTimezoneOffset());

        const options = {
            method: 'get',
            headers,
        };

        const response = await fetch(url, Client4.getOptions(options));

        if (response.ok) {
            return response.json();
        }

        const text = await response.text();

        throw new ClientError(Client4.url, {
            message: text || '',
            status_code: response.status,
            url,
        });
    }

    private doPost = async (url: string, body: any, headers: {[x:string]: string} = {}) => {
        headers['X-Timezone-Offset'] = String(new Date().getTimezoneOffset());

        const options = {
            method: 'post',
            body: JSON.stringify(body),
            headers,
        };

        const response = await fetch(url, Client4.getOptions(options));

        if (response.ok) {
            return response.json();
        }

        const text = await response.text();

        throw new ClientError(Client4.url, {
            message: text || '',
            status_code: response.status,
            url,
        });
    }

    private doPut = async (url: string, body: any, headers: {[x:string]: string} = {}) => {
        headers['X-Timezone-Offset'] = String(new Date().getTimezoneOffset());

        const options = {
            method: 'put',
            body: JSON.stringify(body),
            headers,
        };

        const response = await fetch(url, Client4.getOptions(options));

        if (response.ok) {
            return response.json();
        }

        const text = await response.text();

        throw new ClientError(Client4.url, {
            message: text || '',
            status_code: response.status,
            url,
        });
    }

    private doDelete = async (url: string, headers: {[x:string]: string} = {}) => {
        headers['X-Timezone-Offset'] = String(new Date().getTimezoneOffset());

        const options = {
            method: 'delete',
            headers,
        };

        const response = await fetch(url, Client4.getOptions(options));

        if (response.ok) {
            return response.json();
        }

        const text = await response.text();

        throw new ClientError(Client4.url, {
            message: text || '',
            status_code: response.status,
            url,
        });
    }
}
