// 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 {
    Component,
    ViewChild,
    AfterViewChecked,
    Output,
    EventEmitter,
    Input,
    OnInit,
    ChangeDetectorRef,
} from '@angular/core';
import { NgForm } from '@angular/forms';

import { User } from '../../../base/left-side-nav/user/user';
import { isEmptyForm } from '../../units/shared.utils';
import { SessionService } from '../../services/session.service';

@Component({
    selector: 'new-user-form',
    templateUrl: 'new-user-form.component.html',
    styleUrls: ['./new-user-form.component.scss', '../../../common.scss'],
})
export class NewUserFormComponent implements AfterViewChecked, OnInit {
    showNewPwd: boolean = false;
    showConfirmPwd: boolean = false;

    @Input() isSelfRegistration = false;
    // Notify the form value changes
    @Output() valueChange = new EventEmitter<boolean>();
    @ViewChild('newUserFrom', { static: true }) newUserForm: NgForm;
    newUser: User = new User();
    newUserFormRef: NgForm;
    confirmedPwd: string;
    timerHandler: any;
    validationStateMap: any = {};
    mailAlreadyChecked: any = {};
    userNameAlreadyChecked: any = {};
    emailTooltip = 'TOOLTIP.EMAIL';
    usernameTooltip = 'TOOLTIP.USER_NAME';
    formValueChanged = false;

    checkOnGoing: any = {};
    constructor(
        private session: SessionService,
        private ref: ChangeDetectorRef
    ) {}

    ngOnInit() {
        this.resetState();
    }
    resetState(): void {
        this.showConfirmPwd = false;
        this.showNewPwd = false;
        this.mailAlreadyChecked = {};
        this.userNameAlreadyChecked = {};
        this.emailTooltip = 'TOOLTIP.EMAIL';
        this.usernameTooltip = 'TOOLTIP.USER_NAME';
        this.formValueChanged = false;
        this.checkOnGoing = {
            username: false,
            email: false,
        };
        this.validationStateMap = {
            username: true,
            email: true,
            realname: true,
            newPassword: true,
            confirmPassword: true,
            comment: true,
        };
    }

    public isChecking(key: string): boolean {
        return !this.checkOnGoing[key];
    }

    forceRefreshView(duration: number): void {
        // Reset timer
        if (this.timerHandler) {
            clearInterval(this.timerHandler);
        }
        this.timerHandler = setInterval(() => this.ref.markForCheck(), 100);
        setTimeout(() => {
            if (this.timerHandler) {
                clearInterval(this.timerHandler);
                this.timerHandler = null;
            }
        }, duration);
    }

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

    handleValidation(key: string, flag: boolean): void {
        if (flag) {
            // Checking
            let cont = this.newUserForm.controls[key];
            if (cont) {
                this.validationStateMap[key] = cont.valid;
                // Check email existing from backend
                if (cont.valid && this.formValueChanged) {
                    // Check username from backend
                    if (
                        key === 'username' &&
                        this.newUser.username.trim() !== ''
                    ) {
                        if (
                            this.userNameAlreadyChecked[
                                this.newUser.username.trim()
                            ]
                        ) {
                            this.validationStateMap[key] =
                                !this.userNameAlreadyChecked[
                                    this.newUser.username.trim()
                                ].result;
                            if (!this.validationStateMap[key]) {
                                this.usernameTooltip = 'TOOLTIP.USER_EXISTING';
                            }
                            return;
                        }

                        this.checkOnGoing[key] = true;
                        this.session
                            .checkUserExisting(
                                'username',
                                this.newUser.username
                            )
                            .subscribe(
                                (res: boolean) => {
                                    this.checkOnGoing[key] = false;
                                    this.validationStateMap[key] = !res;
                                    if (res) {
                                        this.usernameTooltip =
                                            'TOOLTIP.USER_EXISTING';
                                    }
                                    this.userNameAlreadyChecked[
                                        this.newUser.username.trim()
                                    ] = {
                                        result: res,
                                    }; // Tag it checked
                                    this.forceRefreshView(2000);
                                },
                                error => {
                                    this.checkOnGoing[key] = false;
                                    this.validationStateMap[key] = false; // Not valid @ backend
                                    this.forceRefreshView(2000);
                                }
                            );
                        return;
                    }

                    // Check email from backend
                    if (key === 'email' && this.newUser.email.trim() !== '') {
                        if (
                            this.mailAlreadyChecked[this.newUser.email.trim()]
                        ) {
                            this.validationStateMap[key] =
                                !this.mailAlreadyChecked[
                                    this.newUser.email.trim()
                                ].result;
                            if (!this.validationStateMap[key]) {
                                this.emailTooltip = 'TOOLTIP.EMAIL_EXISTING';
                            }
                            return;
                        }

                        // Mail changed
                        this.checkOnGoing[key] = true;
                        this.session
                            .checkUserExisting('email', this.newUser.email)
                            .subscribe(
                                (res: boolean) => {
                                    this.checkOnGoing[key] = false;
                                    this.validationStateMap[key] = !res;
                                    if (res) {
                                        this.emailTooltip =
                                            'TOOLTIP.EMAIL_EXISTING';
                                    }
                                    this.mailAlreadyChecked[
                                        this.newUser.email.trim()
                                    ] = {
                                        result: res,
                                    }; // Tag it checked
                                    this.forceRefreshView(2000);
                                },
                                error => {
                                    this.checkOnGoing[key] = false;
                                    this.validationStateMap[key] = false; // Not valid @ backend
                                    this.forceRefreshView(2000);
                                }
                            );
                        return;
                    }

                    // Check password confirmation
                    if (key === 'confirmPassword' || key === 'newPassword') {
                        let cpKey =
                            key === 'confirmPassword'
                                ? 'newPassword'
                                : 'confirmPassword';
                        let peerCont = this.newUserForm.controls[cpKey];
                        if (peerCont && peerCont.valid) {
                            this.validationStateMap['confirmPassword'] =
                                cont.value === peerCont.value;
                        }
                    }
                }
            }
        } else {
            // Reset
            this.validationStateMap[key] = true;
            if (key === 'email') {
                this.emailTooltip = 'TOOLTIP.EMAIL';
            }

            if (key === 'username') {
                this.usernameTooltip = 'TOOLTIP.USER_NAME';
            }
        }
    }

    public get isValid(): boolean {
        let pwdEqualStatus = true;
        if (
            this.newUserForm.controls['confirmPassword'] &&
            this.newUserForm.controls['newPassword']
        ) {
            pwdEqualStatus =
                this.newUserForm.controls['confirmPassword'].value ===
                this.newUserForm.controls['newPassword'].value;
        }
        return (
            this.newUserForm &&
            this.newUserForm.valid &&
            pwdEqualStatus &&
            this.validationStateMap['username'] &&
            this.validationStateMap['email']
        ); // Backend check should be valid as well
    }

    ngAfterViewChecked(): void {
        if (this.newUserFormRef !== this.newUserForm) {
            this.newUserFormRef = this.newUserForm;
            if (this.newUserFormRef) {
                this.newUserFormRef.valueChanges.subscribe(data => {
                    this.formValueChanged = true;
                    this.valueChange.emit(true);
                });
            }
        }
    }

    // Return the current user data
    getData(): User {
        return this.newUser;
    }

    // Reset form
    reset(): void {
        this.resetState();
        if (this.newUserForm) {
            this.newUserForm.reset();
        }
    }

    // To check if form is empty
    isEmpty(): boolean {
        return isEmptyForm(this.newUserForm);
    }
}
