import { GetTaskDetailsCommand } from '../commands/tasks/getTaskDetailsCommand';
import { PerformTaskCommand } from '../commands/tasks/performTaskCommand';
import { GetUserCommand } from '../commands/users/getUserCommand';
import { LoginCommand } from '../commands/users/loginCommand';
import { PerformTaskResult } from '../models/tasks/performTaskResult';
import { SignUpTaskInput } from '../models/tasks/signUpTaskInput';
import { StartContentTaskInput } from '../models/tasks/startContentTaskInput';
import { TaskDetails } from '../models/tasks/taskDetails';
import { TaskInput } from '../models/tasks/taskInput';
import { TaskType } from '../models/tasks/taskType';
import { Watch1MinuteTaskInput } from '../models/tasks/watch1MinuteTaskInput';
import { User } from '../models/users/user';
import { UserInput } from '../models/users/UserInput';
import { PointsWidget } from '../views/pointsWidget';
import { BaseService } from './abstract/baseService';
import cardService from './cardService';
import connectService from './connectService';
import dataService from './dataService';

class UserService extends BaseService {
    public static sharedInstance = new UserService();

    protected logTag: string = 'UserService';

    public async login(userInput: UserInput, suppressAnimation: boolean = false): Promise<User> {
        if (connectService.isConnected) {
            const getUserCommand = new GetUserCommand(userInput);
            const getUserResult = await dataService.executeCommand(getUserCommand);

            if (!getUserResult) {
                this.logout();
                return this.login(userInput, suppressAnimation);
            } else {
                if (connectService.currentUser && connectService.currentUser.externalId !== getUserResult.externalId) {
                    this.logout();
                    return this.login(userInput, suppressAnimation);
                } else {
                    connectService.currentUser = getUserResult;
                    PointsWidget.updateAll();
                    return getUserResult;
                }
            }
        } else {
            const loginCommand = new LoginCommand(userInput);
            const loginResult = await dataService.executeCommand(loginCommand);

            if (!loginResult) {
                throw new Error('Login failed');
            } else {
                connectService.currentUser = loginResult;
                connectService.activePurchases = loginResult.activePurchases;

                const signUpTask = new SignUpTaskInput(TaskType.SignUp);
                signUpTask.shouldAnimate = !suppressAnimation;

                await this.userPerformedTask(signUpTask);

                return loginResult;
            }
        }
    }

    public async logout() {
        connectService.currentUser = null;
        connectService.activePurchases = null;
    }

    public async userPerformedTask(task: TaskInput, rewardCardCallback?: () => void): Promise<PerformTaskResult> {
        return await new Promise(async (resolve) => {
            if (task as Watch1MinuteTaskInput && task.task === TaskType.Watched1Minute) {
                (task as Watch1MinuteTaskInput).isWatchingNonstop = connectService.isWatchingNonstop;
            }
            if (task.shouldAnimate === undefined) {
                task.shouldAnimate = true;
            }
            const performTaskCommand = new PerformTaskCommand(task, (rawResponse) => {
                resolve(new PerformTaskResult(false, rawResponse && rawResponse.data.result && rawResponse.data.result || undefined));
            });
            const performTaskResult = await dataService.executeCommand(performTaskCommand);

            if (!performTaskResult) {
                this.logError(`Performing task ${task.task} for current user resulted in an error`);
                resolve();
            } else {
                connectService.currentUser = performTaskResult.user;
                connectService.hasExceededPointsLimit = performTaskResult.hasExceededLimit;
                connectService.pointsLimitMessage = performTaskResult.limitDescription;

                if (task as StartContentTaskInput && task.task === TaskType.StartContent) {
                    connectService.isWatchingNonstop = (task as StartContentTaskInput).isWatchingNonstop || false;
                }

                if (!task.shouldAnimate) {
                    PointsWidget.updateAll();
                    resolve(new PerformTaskResult(true));
                } else {
                    if (performTaskResult.reward) {
                        await cardService.presentRewardCard(task.task, performTaskResult.reward, rewardCardCallback);
                        PointsWidget.updateAll();
                        resolve(new PerformTaskResult(true));
                    } else {
                        PointsWidget.updateAll();
                        resolve(new PerformTaskResult(true));
                    }
                }
            }
        });

    }

    public async getTaskDetails(taskType: TaskType): Promise<TaskDetails | null> {
        const getTaskDetailsCommand = new GetTaskDetailsCommand(taskType, connectService.currentUser && connectService.currentUser.externalId);
        const taskDetailsResult = await dataService.executeCommand(getTaskDetailsCommand);
        return taskDetailsResult;
    }
}

export default UserService.sharedInstance as UserService;