import { HttpErrorResponse } from "@angular/common/http";
import { Component, OnDestroy, OnInit, TemplateRef } from "@angular/core";
import { FormBuilder, FormGroup, Validators } from "@angular/forms";
import { NgbModal, NgbModalRef } from "@ng-bootstrap/ng-bootstrap";
import { Account } from "@shared/entities";
import { ApiResources, DateHelper, UtilHelper } from "@shared/helpers";
import { IResource, IUserAccount, Page, Pagination } from "@shared/models";
import { AppData, HttpService, NotifyService, ResourceService } from "@shared/services";
import { CompareValidator } from "@shared/validators";
import { finalize, takeUntil } from "rxjs/operators";

class TrackBy {
    account(_: number, item: Account) {
        return item.accountId;
    }

    resource(_: number, item: IResource) {
        return item.id;
    }
}

class FilterOptions {
    fullName: string = null;
    email: string = null;
    mobile: string = null;
    roleId: number = null;
    countryId: number = null;
    active: boolean = null;
    status: string = null;
    lastLoginDate: Date = null;
}

class Filters {
    options: FilterOptions;
    applied: boolean;

    constructor() {
        this.init();
    }

    init() {
        this.options = new FilterOptions();
        this.applied = undefined;
    }
}

@Component({
    templateUrl: "./accounts.html"
})

export class AccountsPage implements OnInit, OnDestroy {
    page: Page;
    today = DateHelper.ngbToday;
    minDate = DateHelper.minDate;

    roles: Array<IResource>;
    loadingRoles: boolean;
    modalRef: NgbModalRef;

    filters: Filters;
    trackBy: TrackBy;
    pagination: Pagination;

    loading: boolean;

    countries: Array<IResource>;
    loadingCountries: boolean;

    users: Array<Account>;
    user: Account;

    loadingUsers: boolean;
    modifying: boolean;
    modifyingContent: string;
    changePasswordForm: FormGroup;
    selectedAccount: Account;
    submitted: boolean;
    submitting: boolean;

    locations: Array<IResource>;
    loadingLocation: boolean;
    locationForm: FormGroup;

    constructor(
        private readonly appData: AppData,
        private readonly resourceService: ResourceService,
        private readonly notifyService: NotifyService,
        private readonly httpService: HttpService,
        private readonly formBuilder: FormBuilder,
        private readonly modalService: NgbModal
    ) {
        this.loading = true;
        this.page = new Page();
        this.filters = new Filters();
        this.trackBy = new TrackBy();
        this.users = new Array<Account>();
        this.roles = new Array<IResource>();
        this.initPagination();
        this.selectedAccount = new Account();
        this.locations = new Array<IResource>();
    }

    private fetchLocations() {
        this.loadingLocation = true;
        this.resourceService.locations()
            .pipe(takeUntil(this.page.unSubscribe))
            .pipe(finalize(() => this.loadingLocation = false))
            .subscribe((response: Array<IResource>) => {
                this.locations = response;
            });
    }

    private buildChangePasswordForm() {
        this.changePasswordForm = this.formBuilder.group({
            accountId: this.selectedAccount.accountId,
            /*password: ["", [Validators.required, Validators.minLength(4)]],*/
            password: ["", [Validators.required, Validators.pattern('(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&].{5,}')]],
            confirmPassword: ["", [Validators.required, Validators.minLength(6)]]
        }, { validator: CompareValidator.compare("password", "confirmPassword") });
    }

    private buildLocationForm() {
        this.locationForm = this.formBuilder.group({
            locationIds: [null, [Validators.required]],
            accountId: [null, [Validators.required]]
        });
        const locationIds = new Array<number>();
        if (UtilHelper.isEmpty(this.selectedAccount.locationIdentifiers)) {
            this.selectedAccount.locationIdentifiers.split(",").forEach((id) => { locationIds.push(+id) });
        }

        this.locationForm.patchValue({
            locationIds: locationIds,
            accountId: this.selectedAccount.accountId
        });
    }

    get forms() {
        return {
            passwordForm: this.changePasswordForm ? this.changePasswordForm.controls : {},
            locationForm: this.locationForm ? this.locationForm.controls : {}
        }
    }

    onChangePassword() {
        this.submitted = true;
        if (!this.changePasswordForm.valid) {
            return;
        }
        if (this.changePasswordForm.controls.password.errors || this.changePasswordForm.controls.confirmPassword.errors) {
            return;
        }

        this.submitting = true;

        const request = this.changePasswordForm.getRawValue();
        request["locationId"] = this.page.userAccount.locationId;
        this.httpService.put<string>(ApiResources.getURI(ApiResources.account.base, ApiResources.account.updatePassword), request)
            .pipe(takeUntil(this.page.unSubscribe))
            .pipe(finalize(() => this.submitted = this.submitting = false))
            .subscribe({
                next: (response: string) => {
                    if (response) {
                        this.notifyService.successToast(response);
                    }
                    this.onCloseModal();
                }, error: (error: HttpErrorResponse) => {
                    const errorMessage = UtilHelper.handleError(error);
                    if (errorMessage) {
                        this.notifyService.warningToast("Error occurred while changing the password.");
                    } else {
                        this.notifyService.defaultError();
                    }
                }
            });
    }

    onModifyLocation() {
        this.submitted = true;
        if (this.locationForm.invalid) {
            this.notifyService.infoToast("Please fill the form.");
            return;
        }
        const request = this.locationForm.getRawValue();
        this.submitting = true;
        this.httpService.post<string>(ApiResources.getURI(ApiResources.account.base, ApiResources.account.modifyLocation), request)
            .pipe(takeUntil(this.page.unSubscribe))
            .pipe(finalize(() => this.submitted = this.submitting = false))
            .subscribe({
                next: (response: string) => {
                    if (response) {
                        this.notifyService.successToast(response);
                    }
                    this.fetchAccounts();
                    this.onCloseModal();
                }, error: (error: HttpErrorResponse) => {
                    const errorMessage = UtilHelper.handleError(error);
                    if (errorMessage) {
                        this.notifyService.warningToast("Error occurred while updating location.");
                    } else {
                        this.notifyService.defaultError();
                    }
                }
            });
    }

    private initPagination() {
        this.pagination = new Pagination();
        this.pagination.pageIndex = 1;
        this.pagination.pageSize = 15;
    }

    private fetchRoles() {
        this.loadingRoles = true;
        this.resourceService.roles()
            .pipe(takeUntil(this.page.unSubscribe))
            .pipe(finalize(() => this.loadingRoles = false))
            .subscribe((response: Array<IResource>) => {
                this.roles = response;
            });
    }

    private fetchCountries() {
        this.loadingCountries = true;
        this.resourceService.countries()
            .pipe(takeUntil(this.page.unSubscribe))
            .pipe(finalize(() => this.loadingCountries = false))
            .subscribe((response: Array<IResource>) => {
                this.countries = response;
            });
    }

    private fetchAccounts() {
        this.loading = true;
        const request = Object.assign(UtilHelper.clone(this.filters.options), UtilHelper.clone(this.pagination));
        this.httpService.post<Array<Account>>(ApiResources.getURI(ApiResources.account.base, ApiResources.account.fetch), request)
            .pipe(takeUntil(this.page.unSubscribe))
            .pipe(finalize(() => this.loading = false))
            .subscribe((response: Array<Account>) => {
                this.users = response;
                UtilHelper.applyPagination(this.users, this.pagination);
            }, (error: HttpErrorResponse) => {
                const errorMessage = UtilHelper.handleError(error);
                if (errorMessage) {
                    this.notifyService.warning("Error occurred while fetching users.");
                } else {
                    this.notifyService.defaultError();
                }
            });
    }

    onModifyStatus(id: number, status: boolean) {
        this.user = this.users.find(m => m.accountId === id);

        this.notifyService.confirm(`Do you really want to ${status ? "enable" : "disable"} this selected account?`, () => {
            this.modifying = true;
            this.modifyingContent = `${status ? "enabling" : "disabling"} the selected account`;

            this.httpService
                .put<string>(ApiResources.getURI(ApiResources.account.base, ApiResources.account.modifyStatus), { accountId: this.user.accountId, active: status, modifiedBy: this.page.userAccount.accountId })
                .pipe(takeUntil(this.page.unSubscribe))
                .pipe(finalize(() => { this.modifying = false; this.user = undefined }))
                .subscribe({
                    next: (response: string) => {
                        this.fetchAccounts();
                        this.notifyService.success(response);
                    }, error: (error: HttpErrorResponse) => {
                        const errorMessage = UtilHelper.handleError(error);
                        if (errorMessage) {
                            this.notifyService.warning(errorMessage);
                        } else {
                            this.notifyService.defaultError();
                        }
                    }
                });
        });

    }

    onModifyLockedStatus(id: number, status: boolean) {
        this.user = this.users.find(m => m.accountId === id);

        this.notifyService.confirm(`Do you really want to ${status ? "lock" : "unlock"} this selected account?`, () => {
            this.modifying = true;
            this.modifyingContent = `${status ? "lock" : "unlock"} the selected account`;

            this.httpService
                .put<string>(ApiResources.getURI(ApiResources.account.base, ApiResources.account.lockStatus), { accountId: this.user.accountId, isLocked: status, modifiedBy: this.page.userAccount.accountId })
                .pipe(takeUntil(this.page.unSubscribe))
                .pipe(finalize(() => { this.modifying = false; this.user = undefined }))
                .subscribe({
                    next: (response: string) => {
                        this.fetchAccounts();
                        this.notifyService.success(response);
                    }, error: (error: HttpErrorResponse) => {
                        const errorMessage = UtilHelper.handleError(error);
                        if (errorMessage) {
                            this.notifyService.warning(errorMessage);
                        } else {
                            this.notifyService.defaultError();
                        }
                    }
                });
        });

    }

    onNextPage() {
        this.fetchAccounts();
        $("body,html").animate({ scrollTop: 0 });
    }

    onShowFilters() {
        $("body").addClass("right-bar-enabled");
    }

    onApplyFilters() {
        this.filters.applied = UtilHelper.isFiltersApplied(this.filters.options);
        this.initPagination();
        this.loading = true;
        this.fetchAccounts();
        this.onCloseFilters();
    }

    onResetFilters() {
        this.filters.init();
        this.loading = true;
        this.onCloseFilters();
        this.fetchAccounts();
    }

    onCloseFilters() {
        $("body").removeClass("right-bar-enabled");
    }

    onOpenLocationModel(account: Account, content: TemplateRef<any>) {
        this.selectedAccount = account;
        this.buildLocationForm();
        this.onOpenModel(content);
    }

    onOpenChangePassword(account: Account, content: TemplateRef<any>) {
        this.selectedAccount = account;
        this.buildChangePasswordForm();
        this.onOpenModel(content);
    }

    onOpenModel(content: TemplateRef<any>) {
        this.modalRef = this.modalService.open(content, {
            backdrop: "static",
            keyboard: false,
            centered: false,
            size: "lg",
            windowClass: "custom-modal effect-scale"
        });
    }

    onCloseModal() {
        try {
            this.modalRef.close();
            this.modalRef = undefined;
        } catch (e) {
            // ignored;
        }
        this.submitted = this.submitting = false;
        this.selectedAccount = new Account();
    }

    ngOnInit() {
        this.appData.userAccount
            .pipe(takeUntil(this.page.unSubscribe))
            .subscribe((userAccount: IUserAccount) => {
                if (userAccount) {
                    this.page.userAccount = userAccount;
                    this.fetchRoles();
                    this.fetchCountries();
                    this.fetchAccounts();
                    this.fetchLocations();
                } else {
                    this.page.userAccount = undefined;
                }
            });
    }

    ngOnDestroy() {
        this.page.unsubscribeAll();
    }

    space(event) {
        console.log(event);
        if (event.charcode === 32) {

            event.preventdefault();
        }
    }
}