// Copyright Project Harbor Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import { AfterViewChecked, Component, OnInit, ViewChild } from '@angular/core';
import { NgForm } from '@angular/forms';
import { NavigationExtras, Router } from '@angular/router';
import { SessionUser } from '../../shared/entities/session-user';
import { SessionService } from '../../shared/services/session.service';
import { MessageHandlerService } from '../../shared/services/message-handler.service';
import { SearchTriggerService } from '../../shared/components/global-search/search-trigger.service';
import { ResetSecret } from './account';
import { CopyInputComponent } from '../../shared/components/push-image/copy-input.component';
import {
    CommonRoutes,
    ConfirmationButtons,
    ConfirmationTargets,
} from '../../shared/entities/shared.const';
import { ConfirmationDialogComponent } from '../../shared/components/confirmation-dialog';
import { InlineAlertComponent } from '../../shared/components/inline-alert/inline-alert.component';
import { ConfirmationMessage } from '../global-confirmation-dialog/confirmation-message';
import { UserService } from 'ng-swagger-gen/services/user.service';
import { AppConfigService } from '../../services/app-config.service';

@Component({
    selector: 'account-settings-modal',
    templateUrl: 'account-settings-modal.component.html',
    styleUrls: ['./account-settings-modal.component.scss', '../../common.scss'],
})
export class AccountSettingsModalComponent implements OnInit, AfterViewChecked {
    opened = false;
    staticBackdrop = true;
    originalStaticData: SessionUser;
    account: SessionUser;
    error: any = null;
    emailTooltip = 'TOOLTIP.EMAIL';
    mailAlreadyChecked = {};
    isOnCalling = false;
    formValueChanged = false;
    checkOnGoing = false;
    RenameOnGoing = false;
    originAdminName = 'admin';
    newAdminName = 'admin@harbor.local';
    renameConfirmation = false;
    showSecretDetail = false;
    resetForms = new ResetSecret();
    showGenerateCli: boolean = false;
    @ViewChild('confirmationDialog')
    confirmationDialogComponent: ConfirmationDialogComponent;

    accountFormRef: NgForm;
    @ViewChild('accountSettingsFrom', { static: true }) accountForm: NgForm;
    @ViewChild('resetSecretFrom', { static: true }) resetSecretFrom: NgForm;
    @ViewChild('accountSettingInlineAlert') inlineAlert: InlineAlertComponent;
    @ViewChild('resetSecretInlineAlert')
    resetSecretInlineAlert: InlineAlertComponent;
    @ViewChild('copyInput') copyInput: CopyInputComponent;
    showInputSecret: boolean = false;
    showConfirmSecret: boolean = false;

    constructor(
        private session: SessionService,
        private msgHandler: MessageHandlerService,
        private router: Router,
        private searchTrigger: SearchTriggerService,
        private userService: UserService,
        private appConfigService: AppConfigService
    ) {}

    private validationStateMap: any = {
        account_settings_email: true,
        account_settings_full_name: true,
    };

    ngOnInit(): void {
        this.refreshAccount();
    }

    refreshAccount() {
        // Value copy
        this.account = Object.assign({}, this.session.getCurrentUser());
        this.originalStaticData = Object.assign(
            {},
            this.session.getCurrentUser()
        );
    }

    ngAfterViewChecked(): void {
        if (this.accountFormRef !== this.accountForm) {
            this.accountFormRef = this.accountForm;
            if (this.accountFormRef) {
                this.accountFormRef.valueChanges.subscribe(data => {
                    if (this.error) {
                        this.error = null;
                    }
                    this.formValueChanged = true;
                    if (this.account.username === this.originAdminName) {
                        this.inlineAlert.close();
                    }
                });
            }
        }
    }

    getValidationState(key: string): boolean {
        return this.validationStateMap[key];
    }

    handleValidation(key: string, flag: boolean): void {
        if (flag) {
            // Checking
            let cont = this.accountForm.controls[key];
            if (cont) {
                this.validationStateMap[key] = cont.valid;
                // Check email existing from backend
                if (cont.valid && key === 'account_settings_email') {
                    if (
                        this.formValueChanged &&
                        this.account.email !== this.originalStaticData.email
                    ) {
                        if (this.mailAlreadyChecked[this.account.email]) {
                            this.validationStateMap[key] =
                                !this.mailAlreadyChecked[this.account.email]
                                    .result;
                            if (!this.validationStateMap[key]) {
                                this.emailTooltip = 'TOOLTIP.EMAIL_EXISTING';
                            }
                            return;
                        }

                        // Mail changed, if self-registration disabled, only system admin can check mail-existing status
                        if (
                            this.session.getCurrentUser()?.has_admin_role ||
                            this.appConfigService.getConfig()?.self_registration
                        ) {
                            this.checkOnGoing = true;
                            this.session
                                .checkUserExisting('email', this.account.email)
                                .subscribe(
                                    (res: boolean) => {
                                        this.checkOnGoing = false;
                                        this.validationStateMap[key] = !res;
                                        if (res) {
                                            this.emailTooltip =
                                                'TOOLTIP.EMAIL_EXISTING';
                                        }
                                        this.mailAlreadyChecked[
                                            this.account.email
                                        ] = {
                                            result: res,
                                        }; // Tag it checked
                                    },
                                    error => {
                                        this.checkOnGoing = false;
                                        this.validationStateMap[key] = false; // Not valid @ backend
                                    }
                                );
                        }
                    }
                }
            }
        } else {
            // Reset
            this.validationStateMap[key] = true;
            this.emailTooltip = 'TOOLTIP.EMAIL';
        }
    }

    isUserDataChange(): boolean {
        if (!this.originalStaticData || !this.account) {
            return false;
        }
        for (let prop in this.originalStaticData) {
            if (this.originalStaticData[prop] !== this.account[prop]) {
                return true;
            }
        }
        return false;
    }

    public get isValid(): boolean {
        return (
            this.accountForm &&
            this.accountForm.valid &&
            this.error === null &&
            this.validationStateMap['account_settings_email']
        ); // backend check is valid as well
    }

    public get showProgress(): boolean {
        return this.isOnCalling;
    }

    public get checkProgress(): boolean {
        return this.checkOnGoing;
    }

    public get canRename(): boolean {
        return (
            this.account &&
            this.account.has_admin_role &&
            this.originalStaticData.username === 'admin' &&
            this.account.user_id === 1
        );
    }

    onRename(): void {
        this.account.username = this.newAdminName;
        this.RenameOnGoing = true;
    }

    confirmRename(): void {
        if (this.canRename) {
            this.session.updateAccountSettings(this.account).subscribe(
                () => {
                    this.session.renameAdmin(this.account).subscribe(
                        () => {
                            this.msgHandler.showSuccess(
                                'PROFILE.RENAME_SUCCESS'
                            );
                            this.opened = false;
                            this.logOut();
                        },
                        error => {
                            this.msgHandler.handleError(error);
                        }
                    );
                },
                error => {
                    this.isOnCalling = false;
                    this.error = error;
                    if (this.msgHandler.isAppLevel(error)) {
                        this.opened = false;
                        this.msgHandler.handleError(error);
                    } else {
                        this.inlineAlert.showInlineError(error);
                    }
                }
            );
        }
    }

    // Log out system
    logOut(): void {
        // Naviagte to the sign in router-guard
        // Appending 'signout' means destroy session cache
        let navigatorExtra: NavigationExtras = {
            queryParams: { signout: true },
        };
        this.router.navigate([CommonRoutes.EMBEDDED_SIGN_IN], navigatorExtra);
        // Confirm search result panel is close
        this.searchTrigger.closeSearch(true);
    }

    open() {
        // Keep the initial data for future diff
        this.originalStaticData = Object.assign(
            {},
            this.session.getCurrentUser()
        );
        this.account = Object.assign({}, this.session.getCurrentUser());
        this.formValueChanged = false;

        // Confirm inline alert is closed
        this.inlineAlert.close();

        // Clear check history
        this.mailAlreadyChecked = {};

        // Reset validation status
        this.validationStateMap = {
            account_settings_email: true,
            account_settings_full_name: true,
        };
        this.showGenerateCli = false;
        this.opened = true;
    }

    close() {
        if (this.formValueChanged) {
            if (!this.isUserDataChange()) {
                this.opened = false;
            } else {
                if (this.RenameOnGoing) {
                    this.RenameOnGoing = false;
                    this.opened = false;
                } else {
                    // Need user confirmation
                    this.inlineAlert.showInlineConfirmation({
                        message: 'ALERT.FORM_CHANGE_CONFIRMATION',
                    });
                }
            }
        } else {
            this.opened = false;
        }
    }

    submit() {
        if (!this.isValid || this.isOnCalling) {
            return;
        }

        // Double confirm session is valid
        let cUser = this.session.getCurrentUser();
        if (!cUser) {
            return;
        }

        if (this.RenameOnGoing && !this.renameConfirmation) {
            this.renameConfirmation = true;
            this.inlineAlert.showInlineWarning({
                message: 'PROFILE.RENAME_CONFIRM_INFO',
            });
            return;
        }

        this.isOnCalling = true;

        if (this.RenameOnGoing && this.renameConfirmation) {
            this.confirmRename();
        } else {
            this.session.updateAccountSettings(this.account).subscribe(
                () => {
                    this.isOnCalling = false;
                    this.opened = false;
                    this.msgHandler.showSuccess('PROFILE.SAVE_SUCCESS');
                    // get user info from back-end then refresh account
                    this.session.retrieveUser().subscribe(() => {
                        this.refreshAccount();
                    });
                },
                error => {
                    this.isOnCalling = false;
                    this.error = error;
                    if (this.msgHandler.isAppLevel(error)) {
                        this.opened = false;
                        this.msgHandler.handleError(error);
                    } else {
                        this.inlineAlert.showInlineError(error);
                    }
                }
            );
        }
    }

    confirmNo($event: any): void {
        if (this.RenameOnGoing) {
            this.RenameOnGoing = false;
        }
        if (this.renameConfirmation) {
            this.renameConfirmation = false;
        }
    }

    confirmYes($event: any): void {
        if (this.RenameOnGoing) {
            this.RenameOnGoing = false;
        }
        if (this.renameConfirmation) {
            this.renameConfirmation = false;
        }
        this.inlineAlert.close();
        this.opened = false;
    }

    onSuccess(event) {
        this.inlineAlert.showInlineSuccess({ message: 'PROFILE.COPY_SUCCESS' });
    }

    onError(event) {
        this.inlineAlert.showInlineError({ message: 'PROFILE.COPY_ERROR' });
    }

    generateCli(userId): void {
        let generateCliMessage = new ConfirmationMessage(
            'PROFILE.CONFIRM_TITLE_CLI_GENERATE',
            'PROFILE.CONFIRM_BODY_CLI_GENERATE',
            '',
            userId,
            ConfirmationTargets.TARGET,
            ConfirmationButtons.CONFIRM_CANCEL
        );
        this.confirmationDialogComponent.open(generateCliMessage);
    }

    showGenerateCliFn() {
        this.showGenerateCli = !this.showGenerateCli;
    }

    confirmGenerate(): void {
        this.resetCliSecret(null);
    }

    resetCliSecret(secret) {
        this.userService
            .setCliSecret({
                userId: this.account.user_id,
                secret: secret
                    ? {
                          secret: secret,
                      }
                    : {},
            })
            .subscribe({
                next: res => {
                    if (secret) {
                        this.account.oidc_user_meta.secret = secret;
                    } else {
                        this.userService.getCurrentUserInfo().subscribe(res => {
                            this.account.oidc_user_meta.secret =
                                res?.oidc_user_meta?.secret;
                        });
                    }
                    this.closeReset();
                    this.inlineAlert.showInlineSuccess({
                        message: 'PROFILE.GENERATE_SUCCESS',
                    });
                },
                error: err => {
                    this.resetSecretInlineAlert.showInlineError({
                        message: 'PROFILE.GENERATE_ERROR',
                    });
                },
            });
    }

    disableChangeCliSecret() {
        return (
            this.resetSecretFrom.invalid ||
            this.resetSecretFrom.value.input_secret !==
                this.resetSecretFrom.value.confirm_secret
        );
    }

    closeReset() {
        this.showSecretDetail = false;
        this.showGenerateCliFn();
        this.resetSecretFrom.resetForm(new ResetSecret());
    }

    openSecretDetail() {
        this.showSecretDetail = true;
        this.resetSecretInlineAlert.close();
    }
}
