import { AfterViewInit, ChangeDetectorRef, Component, OnInit, ViewChild } from '@angular/core';
import { NavigationEnd, Router } from '@angular/router';
import { RouterExtensions } from '@nativescript/angular';
import { ApplicationSettings, ImageSource } from '@nativescript/core';
import { action } from '@nativescript/core/ui/dialogs';
import { Actions, ofActionErrored, Select, Store } from '@ngxs/store';
import { Feedback, FeedbackType } from 'nativescript-feedback';
import { device, isAndroid } from 'nativescript-platform';
import { DrawerTransitionBase, RadSideDrawer, SlideInOnTopTransition } from 'nativescript-ui-sidedrawer';
import { RadSideDrawerComponent } from 'nativescript-ui-sidedrawer/angular';
import { Observable } from 'rxjs';
import { filter, withLatestFrom } from 'rxjs/operators';
import { erroredActions } from '~/constants/action-errored-catch.constants';
import { APP_ID, wsUserManagement } from '~/constants/api.constants';
import { USER_ID, USER_ROLE, USER_ROLE_ID, USER_TOKEN, USER_UNIT_ID } from '~/constants/login-constants';
import { transitionOptions } from '~/constants/shared.constants';
import { LoginUpdate } from '~/core/entities/input/loginUpdate';
import { UserRoleAll } from '~/core/entities/input/userRoleAll';
import { LoginData } from '~/core/entities/login-data';
import { LoginUpdateOutput } from '~/core/entities/output/loginUpdateOutput';
import { UserRoleAllOutput } from '~/core/entities/output/userRoleAllOutput';
import { LoginDataService } from '~/core/services/login-data.service';
import { WebApiService } from '~/core/services/web-api.service';
import { LoadPersonPhoto, LoadUserRoleAll, StartRoleLoading, StopRoleLoading } from '~/core/state-mgmt/app.actions';
import { AppState } from '~/core/state-mgmt/app.state';
import { AdvertisingAllowedFunctionTypes } from './pages/advertising/utils/allowedAdvertisingFunctionTypes';
import { ApplicationAllowedFunctionTypes } from './pages/application/utils/allowedApplicationFunctionTypes';
import { EvaluationModuleInterface } from './pages/evaluation/EvaluationModuleInterface';
import { MaterialAllowedFunctionTypes } from './pages/material/utils/allowedMaterialFunctionTypes';

@Component({
    selector: 'ns-app',
    templateUrl: 'app.component.html'
})
export class AppComponent implements AfterViewInit, OnInit {
    get sideDrawerTransition(): DrawerTransitionBase {
        return this._sideDrawerTransition;
    }

    @ViewChild(RadSideDrawerComponent, { static: false }) drawerComponent: RadSideDrawerComponent;
    @Select(AppState.getPersonPhotoResource) personPhotoResource$: Observable<ImageSource>;
    @Select(AppState.getUserRoleAll) userRoleAll$: Observable<UserRoleAllOutput[]>;
    @Select(AppState.isRoleLoading) isRoleLoading$: Observable<boolean>;
    userData: LoginData | null = null;
    feedback: Feedback;
    personPhotoResource: ImageSource = null;
    roleDialogOpen = false;
    hasNotch: boolean;
    othersIsLoadingApp: boolean;
    hasMaterialAllowedFunctionType: boolean;        // Material page permission
    m: MaterialAllowedFunctionTypes;
    private drawer: RadSideDrawer;

    private _activatedUrl: string;
    private _sideDrawerTransition: DrawerTransitionBase;

    constructor(
        private router: Router,
        private routerExtensions: RouterExtensions,
        private loginDataSrv: LoginDataService,
        private webApiSrv: WebApiService,
        private store: Store,
        private actions$: Actions,
        private _changeDetectionRef: ChangeDetectorRef
    ) {
        this.feedback = new Feedback();
        this.router.events.subscribe((val) => {
            if (val instanceof NavigationEnd) {
                this.othersIsLoadingApp = false;
            }
        });
    }

    get displayBottomNavigation() {
        return this.userData && this._activatedUrl !== '/home' && this._activatedUrl !== '/login';
    }

    ngAfterViewInit() {
        this.drawer = this.drawerComponent.sideDrawer;
        this._changeDetectionRef.detectChanges();
    }

    isPageSelected(url: string): boolean {
        return this._activatedUrl.startsWith(url);
    }

    onNavItemTap(navItemRoute: string): void {
        this.routerExtensions.navigate([navItemRoute], { transition: transitionOptions });

        this.drawer.closeDrawer();
    }

    onUnitNavItemTap(): void {
        this.routerExtensions.navigate(['/unit', ApplicationSettings.getNumber(USER_UNIT_ID)], {
            transition: {
                name: 'fade'
            }
        });

        this.drawer.closeDrawer();
    }

    onLogIn(): void {
        this.drawer.closeDrawer();
        this.routerExtensions.navigate(['/login'], { clearHistory: true, transition: transitionOptions });
    }

    onLogOut(): void {
        this.loginDataSrv.logout();
        this.drawer.closeDrawer();
        this.routerExtensions.navigate(['/home'], { clearHistory: true, transition: transitionOptions });
        MaterialAllowedFunctionTypes.firstTimeRole = true;  // because of MAJETEK permission
        ApplicationAllowedFunctionTypes.firstTimeRole = true;
        AdvertisingAllowedFunctionTypes.firstTimeRole = true;
    }

    onDrawerButtonTap(): void {
        this.drawer.showDrawer();
    }

    onDrawerOpening() {
        this.hasMaterialAllowedFunctionType = MaterialAllowedFunctionTypes.getPermission();     // Material page permission
    }

    async ngOnInit() {
        this.othersIsLoadingApp = false;
        if (isAndroid()) {
            // determine if android phone has a notch (i.e. Pixel 3XL) by looking at status bar height
            // result is height
            this.hasNotch = device.notch;
        }
        this.roleDialogOpen = false;
        this._activatedUrl = '/home';
        this._sideDrawerTransition = new SlideInOnTopTransition();

        this.router.events
            .pipe(filter((event: any) => event instanceof NavigationEnd))
            .subscribe((event: NavigationEnd) => this._activatedUrl = event.urlAfterRedirects);
        try {
            await this.loginDataSrv.loginData.subscribe(async (data: LoginData | null) => {
                setTimeout(() => {
                    this.userData = data;
                });
                if (this.userData) {
                    this.store.dispatch(new LoadPersonPhoto(this.userData.userId));
                }

                // Material and application page permission
                if (data && MaterialAllowedFunctionTypes.firstTimeRole) {
                    this.store.dispatch(new LoadUserRoleAll())
                        .pipe(withLatestFrom(this.userRoleAll$))
                        .subscribe(([_, result]) => {
                            const role = result.find(r => r.ID === ApplicationSettings.getNumber(USER_ROLE_ID));
                            if (role) {
                                MaterialAllowedFunctionTypes.setPermission(role.DisplayName);
                                this.hasMaterialAllowedFunctionType = MaterialAllowedFunctionTypes.getPermission();
                                MaterialAllowedFunctionTypes.firstTimeRole = false;

                                if (ApplicationAllowedFunctionTypes.firstTimeRole) {
                                    ApplicationAllowedFunctionTypes.setPermission(role.DisplayName);
                                    ApplicationAllowedFunctionTypes.firstTimeRole = false;
                                }

                                if (AdvertisingAllowedFunctionTypes.firstTimeRole) {
                                    AdvertisingAllowedFunctionTypes.setPermission(role.DisplayName);
                                    AdvertisingAllowedFunctionTypes.firstTimeRole = false;
                                }
                            }
                        });
                }
            });
        } catch (e) {
            console.error(e);
        }
        setTimeout(() => {
            this.loginDataSrv.updateUserData();
        });

        this.personPhotoResource$.subscribe((personPhotoResource: ImageSource) => this.personPhotoResource = personPhotoResource);

        this.actions$.pipe(ofActionErrored(...erroredActions.map(eA => eA.action)))
            .subscribe((subscribedAction) => {
                const selectedAction = erroredActions.find((eA) => eA.name === subscribedAction.constructor.name);
                this.feedback.show({
                    title: 'ERROR',
                    type: FeedbackType.Error,
                    message: selectedAction ? selectedAction.message : 'Vyskytla se chyba při komunikaci se službou skautIS. Zkontrolujte připojení nebo to zkuste později.'
                });
            });
    }

    onNavTap(navRoute: string): void {
        if (this._activatedUrl === navRoute) {
            return;
        }
        this.othersIsLoadingApp = true;
        setTimeout(() => {
            this.routerExtensions.navigate([navRoute], { transition: transitionOptions });
        }, 10);
    }

    onUnitNavTap(): void {
        if (this._activatedUrl === '/unit/' + ApplicationSettings.getNumber(USER_UNIT_ID)) {
            return;
        }
        this.othersIsLoadingApp = true;
        setTimeout(() => {
            this.routerExtensions.navigate(['/unit', ApplicationSettings.getNumber(USER_UNIT_ID)], { transition: transitionOptions });
        }, 10);
    }

    async onUserRoleTap() {
        this.store.dispatch(new StartRoleLoading());
        const userRoleAll = new UserRoleAll(APP_ID, ApplicationSettings.getString(USER_TOKEN), ApplicationSettings.getNumber(USER_ID));
        let userRoleAllOutput: UserRoleAllOutput[] = [];
        try {
            userRoleAllOutput = await this.webApiSrv.call(userRoleAll, () => new UserRoleAllOutput(), wsUserManagement).toPromise();
        } catch (e) {
            console.error(e);
        }
        const roles = userRoleAllOutput.map(obj => ({ key: obj.ID, label: obj.DisplayName }));

        const options = {
            message: 'Vyberte roli',
            cancelButtonText: 'Zrušit',
            actions: roles.map(obj => obj.label)
        };

        this.store.dispatch(new StopRoleLoading());

        if (!this.roleDialogOpen) {
            this.roleDialogOpen = true;
            action(options).then(async (result) => {
                const role = roles.find(obj => obj.label === result);
                if (role) {
                    const loginUpdateRole = new LoginUpdate(APP_ID, ApplicationSettings.getString(USER_TOKEN), role.key);
                    let logUpdOut: LoginUpdateOutput[] = [];
                    try {
                        logUpdOut = await this.webApiSrv.call(loginUpdateRole, () => new LoginUpdateOutput(), wsUserManagement).toPromise();
                    } catch (e) {
                        console.error(e);
                    }
                    setTimeout(() => {
                        ApplicationSettings.setNumber(USER_ROLE_ID, role.key);
                        ApplicationSettings.setString(USER_ROLE, role.label);
                        if (logUpdOut.length > 0) {
                            ApplicationSettings.setNumber(USER_UNIT_ID, logUpdOut[0].ID_Unit);
                            // material page permission
                            MaterialAllowedFunctionTypes.setPermission(role.label);
                            this.hasMaterialAllowedFunctionType = MaterialAllowedFunctionTypes.getPermission();

                            // evaluation permission update
                            EvaluationModuleInterface.changePersonalEvalPermission(this.store, role.label);

                            ApplicationAllowedFunctionTypes.setPermission(role.label);
                            AdvertisingAllowedFunctionTypes.setPermission(role.label);
                        }
                        this.loginDataSrv.updateUserData();
                    });
                }
                this.roleDialogOpen = false;
            });
        }
    }
}
