import { Component, OnInit } from '@angular/core';
import { NavigationEnd, Router } from '@angular/router';
import { RouterExtensions } from '@nativescript/angular';
import { ApplicationSettings } from '@nativescript/core';
import * as dialogs from '@nativescript/core/ui/dialogs';
import { Select, Store } from '@ngxs/store';
import { Feedback, FeedbackType } from 'nativescript-feedback';
import { Observable } from 'rxjs';
import { APP_ID, wsEvaluation } from '~/constants/api.constants';
import { USER_TOKEN, USER_UNIT_ID } from '~/constants/login-constants';
import { transitionOptions } from '~/constants/shared.constants';
import { PickerProvider } from '~/core/entities/picker-provider';
import { AuthGuard } from '~/core/services/auth-guard.service';
import { WebApiService } from '~/core/services/web-api.service';
import { UnitDetailOutput } from '~/pages/unit/entities/output/unitDetailOutput';
import { LoadUnitDetail } from '~/pages/unit/state-mgmt/unit-detail.actions';
import { UnitDetailState } from '~/pages/unit/state-mgmt/unit-detail.state';
import { EvaluationSearchMyUnit } from '../../entities/evaluation-search-myUnit';
import { EvaluationSubtypeAll } from '../../entities/input/allEvalSubtypes';
import { EvaluationAll } from '../../entities/input/evaluationAll';
import { EvaluationPersonInsert } from '../../entities/input/personInsertInput';
import { AllSubtypesOutput } from '../../entities/output/allEvalSubtypesOutput';
import { EvaluationAllOutput } from '../../entities/output/evaluationAllOutput';
import { PersonInsertOutput } from '../../entities/output/personInsertOutput';
import { EvaluationSubtypesState } from '../../state-mgmt/evaluation-all-eval-subtypes.state';
import { LoadAnswersIndividual } from '../../state-mgmt/evaluation-answers-individual.actions';
import { AnswersIndividualState } from '../../state-mgmt/evaluation-answers-individual.state';
import { SearchEvaluationListState } from '../../state-mgmt/evaluation-list.state';
import { EvaluationPermissionState } from '../../state-mgmt/evaluation-permission.state';
import { InsertIndivEvaluationState } from '../../state-mgmt/evaluation-person-insert.state';
import { LoadYearsCreate } from '../../state-mgmt/evaluation-years-create.actions';
import { YearsCreateState } from '../../state-mgmt/evaluation-years-create.state';

@Component({
    selector: 'EvaluationCreate',
    moduleId: module.id,
    templateUrl: './evaluation-create.component.html',
    styleUrls: ['./evaluation-create.component.scss']
})

export class EvaluationCreateComponent implements OnInit {

    @Select(YearsCreateState.getYearsForProvider) yearsCreate$: Observable<PickerProvider[]>;
    @Select(YearsCreateState.isLoading) yearsIsLoading$: Observable<boolean>;

    @Select(UnitDetailState.getUnitDetail) unitDetail$: Observable<UnitDetailOutput>;
    @Select(UnitDetailState.isLoading) unitIsLoading$: Observable<boolean>;

    @Select(SearchEvaluationListState.getEvaluations) searchEvaluations$: Observable<EvaluationAllOutput[]>;
    @Select(SearchEvaluationListState.isLoading) evaluationIsLoading$: Observable<boolean>;

    @Select(InsertIndivEvaluationState.getInsertOutput) insertOutput$: Observable<PersonInsertOutput>;
    @Select(InsertIndivEvaluationState.isLoading) insertIsLoading$: Observable<boolean>;

    @Select(EvaluationSubtypesState.getSubtypes) subtypes$: Observable<AllSubtypesOutput[]>;
    @Select(EvaluationSubtypesState.isLoading) subtypesIsLoading$: Observable<boolean>;

    @Select(EvaluationPermissionState.getEntityNumber) entityNumber: Observable<string>;

    @Select(AnswersIndividualState.isLoading) answersIsLoading$: Observable<boolean>;

    othersIsLoading: boolean;
    unitDetail: UnitDetailOutput;
    searchEvaluations: EvaluationAllOutput[];
    idEvalPerson: number;

    constructor(
        private router: Router,
        private routerExtensions: RouterExtensions,
        private store: Store,
        private authGuard: AuthGuard,
        private webApiSrv: WebApiService
    ) {
        this.router.events.subscribe((val) => {
            if (val instanceof NavigationEnd) {
                if (this.router.url === '/evaluation/create') {
                    this.othersIsLoading = false;
                }
            }
        });
    }

    ngOnInit(): void {
        this.othersIsLoading = false;
        this.store.dispatch(new LoadYearsCreate());
        this.store.dispatch(new LoadUnitDetail(ApplicationSettings.getNumber(USER_UNIT_ID)));

        this.unitDetail$.subscribe(unitDetail => this.unitDetail = unitDetail);
    }

    // Back button action
    onBackButton(): void {
        this.routerExtensions.back();
    }

    // Returns right evaluation subtype ID based on unit type
    getIdEvalSubtype() {
        let allSubtypes: AllSubtypesOutput[];
        this.subtypes$.subscribe(subtypes => allSubtypes = subtypes);
        for (const i of allSubtypes) {
            if (i.UnitType === this.unitDetail.UnitType) {
                return i.ID;
            }
        }
        return 0;
    }

    // Turns off loading and throws accoring feedback/dialog for situation occured in onCreate on top of the screen
    throwFeedback(dialogType, year = 0) {
        // Shows feedback if we
        function showFeedback(title: string, type: FeedbackType, message: string): void {
            new Feedback().show({
                title,
                type,
                duration: 5000,
                message
            });
        }

        this.othersIsLoading = false;
        switch (dialogType) {
            case 'timeoutExpired': {
                showFeedback('Vypršel timeout', FeedbackType.Error, 'Časový limit pro operaci vypršel.');
                break;
            }

            case 'badUnitNumber': {
                showFeedback('Špatná jednotka', FeedbackType.Error, 'Číslo jednotky pro hodnocení nesedí s vámi zvolenou rolí.');
                break;
            }

            case 'moreEvalsFound': {
                showFeedback('Nalezeno více celkových hodnocení', FeedbackType.Error,
                `Celkové hodnocení jednotky ${this.unitDetail.RegistrationNumber} - ${this.unitDetail.DisplayName} pro rok ${year} bylo založeno vícekrát - kontaktujte administrátora.`);
                break;
            }

            case 'evalClosed': {
                showFeedback('Hodnocení uzavřeno', FeedbackType.Error,
                `Celkové hodnocení jednotky ${this.unitDetail.RegistrationNumber} - ${this.unitDetail.DisplayName} pro rok ${year} již bylo uzavřeno.`);
                break;
            }

            case 'insertFailed': {
                showFeedback('Založení hodnocení selhalo', FeedbackType.Error,
                'Individuální hodnocení jednotky nebylo úspěšně založeno - opakujte akci.');
                break;
            }

            case 'subTypeNotFound': {
                showFeedback('Založení hodnocení selhalo', FeedbackType.Error,
                'Individuální hodnocení jednotky nebylo úspěšně založeno - nebyl nalezen správný podtyp.');
                break;
            }

            case 'indEvalExists': {
                dialogs.confirm({
                    title: 'Individuální hodnocení je již založeno',
                    message: `Individuální hodnocení jednotky ${this.unitDetail.RegistrationNumber} - ${this.unitDetail.DisplayName} pro rok ${year} již bylo založeno. Přejete si editovat?`,
                    okButtonText: 'Ano',
                    cancelButtonText: 'Ne'
                }).then(result => {
                    if (result) {
                        this.loadAnswers();
                    }
                });
                break;
            }
        }
    }

    // Gets the right subtype for current unit
    getRightSubtype(evaluationSubtypes: AllSubtypesOutput[]): number {
        for (const i of evaluationSubtypes) {
            if (i.UnitType === this.unitDetail.UnitType) {
                return i.ID;
            }
        }
        return 0;
    }
    // Returns main evaluations of current unit by year
    getMainEvaluation(year: number) {
        return this.webApiSrv.call(
            new EvaluationAll(APP_ID, ApplicationSettings.getString(USER_TOKEN), '', year, 'MyUnit'),
            () => new EvaluationAllOutput(),
            wsEvaluation
        ).toPromise();
    }
    // Returns all possible evaluation subtypes
    getAllSubtypes() {
        return this.webApiSrv.call(
            new EvaluationSubtypeAll(APP_ID, ApplicationSettings.getString(USER_TOKEN)),
            () => new AllSubtypesOutput(),
            wsEvaluation
        ).toPromise();
    }
    // Inserts new personal evaluation by year and subtype
    callPersonInsert(subtype: number, year: number) {
        return this.webApiSrv.call(
            new EvaluationPersonInsert(APP_ID, ApplicationSettings.getString(USER_TOKEN), subtype, year),
            () => new PersonInsertOutput(),
            wsEvaluation
        ).toPromise();
    }

    // Creates new individual evaluation
    async onCreate(evaluationCreate: EvaluationSearchMyUnit) {
        this.othersIsLoading = true;
        this.authGuard.canActivate();
        let unitNum;
        this.entityNumber.subscribe(entityNumber => unitNum = entityNumber);
        if (unitNum !== this.unitDetail.RegistrationNumber) {
            this.throwFeedback('badUnitNumber', evaluationCreate.year);
            return;
        }

        const evaluationAll: EvaluationAllOutput[] = await this.getMainEvaluation(evaluationCreate.year);
        // There is supposed to be only one evaluation per year
        if (evaluationAll.length > 1) {
            this.throwFeedback('moreEvalsFound', evaluationCreate.year);
            return;
        }

        let rightSubtype = 0;
        // Main evaluation does not exist, we need to get all eval subtypes and find the right one
        if (evaluationAll.length === 0) {
            const evaluationSubtypes: AllSubtypesOutput[] = await this.getAllSubtypes();

            rightSubtype = this.getRightSubtype(evaluationSubtypes);
        } else {
            // Main evaluation already existed - we can get subtype from it
            rightSubtype = evaluationAll[0].ID_EvaluationSubtype;
        }
        // Right subtype not found
        if (rightSubtype === 0) {
            this.throwFeedback('subTypeNotFound', evaluationCreate.year);
            return;
        }
        // Evaluation is already closed
        if (evaluationAll.length !== 0 && evaluationAll[0].ID_EvaluationState === 'closed') {
            this.throwFeedback('evalClosed', evaluationCreate.year);
            return;
        }

        this.idEvalPerson = 0;
        // Main or personal evaluation does not exist - we have to create it
        if (evaluationAll.length === 0 || evaluationAll[0].ID_EvaluationPerson === 0) {
            const insertResult: PersonInsertOutput[] = await this.callPersonInsert(rightSubtype, evaluationCreate.year);
            // There is supposed to be only one result
            if (insertResult.length !== 1) {
                this.throwFeedback('insertFailed', evaluationCreate.year);
                return;
            }

            this.idEvalPerson = insertResult[0].ID;
            this.loadAnswers();
        } else {
            // Personal evaluation does exist - we get its ID
            this.idEvalPerson = evaluationAll[0].ID_EvaluationPerson;
            this.throwFeedback('indEvalExists', evaluationCreate.year);
        }
    }

    // Loads answers after onCreate action and routes on survey page
    loadAnswers() {
        // Limits in ms
        const oneCallLimit = 100; // 100 ms
        const loadAnswersLimit = 5000; // 5 sec

        if (this.idEvalPerson != 0) {
            this.store.dispatch(new LoadAnswersIndividual(this.idEvalPerson));
            let answersLoading = true;

            // We kill answers loading in case it takes more than 'loadAnswersLimit'
            const operationTimer = setTimeout(() => {
                this.othersIsLoading = false;
                clearInterval(loadAnswersInterval);
                this.throwFeedback('timeoutExpired');
                return;
            }, loadAnswersLimit);

            const loadAnswersInterval = setInterval(() => {
                this.answersIsLoading$.subscribe(loading => answersLoading = loading);
                // We will wait until the answers are loaded
                if (!answersLoading) {
                    this.routerExtensions.navigate(['evaluation/create', this.idEvalPerson], { transition: transitionOptions });
                    clearInterval(loadAnswersInterval);
                    clearTimeout(operationTimer);
                    this.othersIsLoading = false;
                }
            }, oneCallLimit);
        }
    }
}
