import { Component, OnInit, ViewChild } from '@angular/core';
import { NgForm } from '@angular/forms';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { ConfirmationService } from 'primeng/api';
import { Dialog } from 'primeng/dialog';
import { combineLatest } from 'rxjs/observable/combineLatest';
import { Subscription } from 'rxjs/Subscription';
import { AppService } from '../../app.service';
import { ASSOCS, SERVICES } from '../../constants';
import { MessageTemplateModel } from '../../models/messageTemplate.model';
import { ResponseActionEnum } from '../../models/responseAction.enum';
import { StageTypeEnum } from '../../models/stageType.enum';
import { StepTypeEnum } from '../../models/stepType.enum';
import { WorkflowMessageModel } from '../../models/workflowMessage.model';
import { WorkflowStageModel } from '../../models/workflowStage.model';
import { WorkflowStepModel } from '../../models/workflowStep.model';
import { WorkflowTemplateModel } from '../../models/workflowTemplate.model';
import { SaveService } from '../../oneit/services/save.service';
import { UtilsService } from '../../oneit/services/utils.service';
import { AdminPortalLayoutService } from '../admin-portal/admin-portal-layout/admin-portal-layout.service';
import { BaseComponent } from '../base/base.component';
import { EditWorkflowTemplateService } from './edit-workflow-template.service';

@Component({
    selector: 'app-edit-workflow-template',
    templateUrl: './edit-workflow-template.component.html',
    styleUrls: ['./edit-workflow-template.component.scss']
})
export class EditWorkflowTemplateComponent extends BaseComponent implements OnInit {

    subscriptions: Array<Subscription> = [];
    @ViewChild('form') form: NgForm;
    @ViewChild('messageTemplateDialog') messageTemplateDialog: Dialog;
    createdObjs;
    updatedObjs;
    deletedObjs;

    workflow = new WorkflowTemplateModel();
    preStages: Array<WorkflowStageModel> = [];
    genericStages: Array<WorkflowStageModel> = [];
    postStages: Array<WorkflowStageModel> = [];
    stageTypes: Array<StageTypeEnum>;
    stepTypeOptions: Array<StepTypeEnum>;
    stepTypes: Array<StepTypeEnum>;
    responseActions: Array<ResponseActionEnum>;
    messageTemplates: Array<MessageTemplateModel> = [];
    editField: any = {};
    showLoader = false;
    editMode = false;
    displayNewMessageTemplate = false;
    selectedTemplateID = null;
    selectedMessageTemplate = null; // object id of type WorkflowMessageModel

    constructor(
        private router: Router,
        private activatedRoute: ActivatedRoute,
        private utilsService: UtilsService,
        private saveService: SaveService,
        private appService: AppService,
        private editWorkflowTemplateService: EditWorkflowTemplateService,
        private adminPortalLayoutService: AdminPortalLayoutService,
        private confirmationService: ConfirmationService
    ) {
        super(utilsService);
    }

    ngOnInit(): void {
        this.createdObjs = {};
        this.updatedObjs = {};
        this.deletedObjs = {};
        this.showLoader = true;

        this.utilsService.resetCounter();

        this.startComponent();
        this.subscriptions.push(this.adminPortalLayoutService.userDataUpdated
            .subscribe(
                () => {
                    this.startComponent();
                }
            ));
    }

    startComponent(): void {

        if (!this.adminPortalLayoutService.userData || this.alreadyExists()) {
            return;
        }

        this.subscriptions.push(this.activatedRoute.params
            .subscribe(
                (params: Params) => {
                    this.subscriptions.push(
                        combineLatest(
                            this.appService.getTypes('StageTypes'),
                            this.appService.getTypes('StepTypes', true, 'Add Step'),
                            this.appService.getTypes('ResponseActions')
                        )
                            .subscribe(response => {

                                if (this.alreadyExists()) {
                                    return;
                                }

                                this.stageTypes = response[0];
                                this.stepTypeOptions = response[1];

                                if (!this.adminPortalLayoutService.getHiringTeamID().HasDiversity) {
                                    this.stepTypeOptions = this.stepTypeOptions.filter(type => type.Value !== 'DIVERSITY');
                                }

                                this.responseActions = response[2];
                                if (params.id && params.id !== '0') {
                                    this.editMode = true;
                                    this.getWorkflowTemplateByID(params.id);
                                } else {
                                    this.editMode = false;
                                    this.utilsService.createObject(this.workflow, this.createdObjs);
                                    this.setDefaults();
                                    this.addDefaultStages();
                                    this.stepTypes = [...this.stepTypeOptions];
                                    this.showLogs();
                                    this.showLoader = false;
                                }
                            })
                    );
                }
            ));
        this.getMessageTemplates();
    }

    setDefaults(): void {
        this.workflow.TemplateName = 'Workflow Template';
        this.workflow.LandingButtonText = 'Apply Now';
        this.workflow.VerificationHeaderText = 'Please verify your identity before applying to:';
        this.workflow.ThankYouHeaderText = 'Thank You for your application to:';
        this.workflow.ThankYouSecondaryText = 'We will get in touch with you soon';
        this.workflow.ApplicationButtonText = 'Submit';
        this.workflow.JobOutlineButtonText = 'Continue';
        this.workflow.CaptureCV = true;
        this.workflow.CaptureCL = true;
        this.workflow.HiringTeam = this.adminPortalLayoutService.getHiringTeamID().ObjectID;
    }

    addDefaultStages(): void {
        this.workflow.WorkFlowStages = [];
        let topIndex = 1;
        this.stageTypes.forEach(stage => {
            const index = (!stage.IsPostStage && !stage.IsPreStage) ? topIndex++ : -1;
            const stageObject = new WorkflowStageModel(`${stage.Description} Stage`, false, stage, index,
                false, this.workflow.ObjectID);
            this.utilsService.addMultiRefObject(stageObject, this.workflow, 'WorkFlowStages', this.createdObjs);
        });
        this.reOrderStages(this.filterStages(true, true, true));
    }

    reOrderStages(callback): void {
        this.showLoader = true;
        this.workflow.WorkFlowStages
            .sort((a, b) => a.SortOrder < b.SortOrder ? -1 : a.SortOrder > b.SortOrder ? 1 : 0);
        if (callback) {
            callback();
        }
        this.showLoader = false;
    }

    isGeneric(stage: WorkflowStageModel): boolean {
        return !stage.StageType.IsPostStage && !stage.StageType.IsPreStage;
    }

    filterStages(pre = false, generic = false, post = false): void {
        if (pre) {
            this.preStages = this.filteredStages(true, false);
        }
        if (generic) {
            this.genericStages = this.filteredStages(false, false)
                .sort((a, b) => a.SortOrder > b.SortOrder ? -1 : a.SortOrder < b.SortOrder ? 1 : 0);
        }
        if (post) {
            this.postStages = this.filteredStages(false, true);
        }
    }

    showSaveBtn(): boolean {
        return this.workflow && this.workflow.ObjectID ?
            (this.workflow.HiringTeam === this.adminPortalLayoutService.getHiringTeamID().ObjectID ||
                !this.editMode) : false;
    }

    showSaveCopyBtn(): boolean {
        return this.workflow && this.workflow.ObjectID ?
            (!!this.workflow.HiringTeam) : false;
    }

    getStageHeader(stage: WorkflowStageModel): string {
        return `${stage.Name} (${stage.Steps ? stage.Steps.length : 0})`;
    }

    showLabel(id: string): boolean {
        return (this.editField[id] === undefined || this.editField[id] === false);
    }

    showStageLabel(id: string, stage: WorkflowStageModel): boolean {
        if (stage.StageType.IsPreStage || stage.StageType.IsPostStage) {
            return true;
        } else {
            return this.showLabel(id);
        }
    }

    showStageInput(id: string, stage: WorkflowStageModel): boolean {
        if (stage.StageType.IsPreStage || stage.StageType.IsPostStage) {
            return false;
        } else {
            return this.showInput(id);
        }
    }

    showDelayInput(id: string, message: WorkflowMessageModel): boolean {
        if (!message.Delay || message.Delay === '') {
            return true;
        } else {
            return this.editField ? (this.editField[id] ? this.editField[id] : true) : true;
        }
    }

    showDelayLabel(id: string, message: WorkflowMessageModel): boolean {
        if (!message.Delay || message.Delay === '') {
            return false;
        } else {
            return this.editField ? (this.editField[id] ? this.editField[id] : false) : false;
        }
    }

    filteredStages(isPreStage: boolean, isPostStage: boolean): Array<WorkflowStageModel> {
        return this.workflow && this.workflow.WorkFlowStages ? (
            (!isPreStage && !isPostStage) ?
                this.workflow.WorkFlowStages.filter(stage => !stage.StageType.IsPreStage && !stage.StageType.IsPostStage)
                : isPreStage ?
                this.workflow.WorkFlowStages.filter(stage => stage.StageType.IsPreStage) :
                isPostStage ?
                    this.workflow.WorkFlowStages.filter(stage => stage.StageType.IsPostStage) : []) : [];
    }

    emailResponseSelected(): boolean {
        return this.workflow.ApplicationResponseAction &&
            this.workflow.ApplicationResponseAction.Value === 'EMAIL_RESPONSE';
    }

    showInput(id: string): boolean {
        return this.editField && !!this.editField[id];
    }

    toggleInput(event, fieldName): void {
        this.utilsService.clearErrorMessages();
        if (event.type === 'blur') {
            if (!this.form.controls[fieldName].invalid) {
                this.editField[fieldName] = false;
            } else {
                this.utilsService.showAllErrorMessages();
            }
        } else {
            this.editField[fieldName] = true;
            setTimeout(() => {
                try {
                    document.getElementById(`${fieldName}Id`)
                        .focus();
                    document.getElementById(`${fieldName}Id`)
                        .getElementsByTagName('input')[0]
                        .focus();
                } catch (e) {
                    // ignore this.
                }
            }, 0);
        }
    }

    toggleStageInput(event, fieldName, stage: WorkflowStageModel): void {
        this.utilsService.clearErrorMessages();
        if (event.type === 'blur') {
            if (!this.form.controls[fieldName].invalid) {
                this.editField[fieldName] = false;
            } else {
                this.utilsService.showAllErrorMessages();
            }
        } else {
            if (stage.StageType.IsPreStage || stage.StageType.IsPostStage) {
                return;
            }
            this.editField[fieldName] = true;
            setTimeout(() => {
                try {
                    document.getElementById(`${fieldName}Id`)
                        .focus();
                    document.getElementById(`${fieldName}Id`)
                        .getElementsByTagName('input')[0]
                        .focus();
                } catch (e) {
                    // ignore this.
                }
            }, 0);
        }
    }

    toggleDelayInput(event, fieldName, message: WorkflowMessageModel): void {
        this.utilsService.clearErrorMessages();
        if (event.type === 'blur') {

            if (!message.Delay || message.Delay === '') {
                return;
            }

            if (!this.form.controls[fieldName].invalid) {
                this.editField[fieldName] = false;
            } else {
                this.utilsService.showAllErrorMessages();
            }
        } else {
            this.editField[fieldName] = true;
            setTimeout(() => {
                try {
                    document.getElementById(`${fieldName}Id`)
                        .focus();
                    document.getElementById(`${fieldName}Id`)
                        .getElementsByTagName('input')[0]
                        .focus();
                } catch (e) {
                    // ignore this.
                }
            }, 0);
        }
    }

    getMessageTemplates(callback = null): void {
        const sharedMsgParams = {
            OnlyGlobalTemplate: true,
            attribsByName: 'Autocomplete'
        };
        const myMsgParams = {
            OnlyGlobalTemplate: false,
            attribsByName: 'Autocomplete'
        };

        this.subscriptions.push(
            combineLatest(
                this.editWorkflowTemplateService.getMessageTemplates([], myMsgParams),
                this.editWorkflowTemplateService.getMessageTemplates([], sharedMsgParams)
            )
                .subscribe(
                    (response: [Array<MessageTemplateModel>, Array<MessageTemplateModel>]) => {
                        const myTemplates = response[0].filter(message => message.HiringTeam ===
                            this.adminPortalLayoutService.getHiringTeamID().ObjectID);
                        this.messageTemplates = [...myTemplates, ...response[1]];
                        this.addNewOptionToMessageTemplate();
                        if (callback) {
                            // called when a new message template is created
                            callback();
                        }
                    },
                    error => {
                        this.utilsService.handleError(error);
                    }
                )
        );
    }

    addNewOptionToMessageTemplate(): void {
        const newTemplate = new MessageTemplateModel();
        newTemplate.TemplateName = 'New Template';
        newTemplate.id = '0';
        this.messageTemplates.push(newTemplate);
    }

    showMessageTemplateDialog($event, message: WorkflowMessageModel): void {
        const template = $event.value;
        if (template.id && template.id === '0') {
            // When new template is selected, store it to global variable
            this.selectedTemplateID = 'NEW';
            this.displayNewMessageTemplate = true;
            this.selectedMessageTemplate = message.ObjectID;
        } else {
            // When other options from message template are selected, assign global variable to null
            this.selectedMessageTemplate = null;
        }
    }

    configureMessageTemplate(message: WorkflowMessageModel): void {
        if (message.MessageTemplate && typeof message.MessageTemplate !== 'string') {
            this.selectedTemplateID = message.MessageTemplate.ObjectID;
            this.displayNewMessageTemplate = true;
        }
    }

    closeDialog(value): void {
        if (value === -1) {
            // Cancel button is clicked
        } else if (this.selectedTemplateID === 'NEW' && this.selectedMessageTemplate !== null) {
            // When new value is created, assign it to the current WorkFlowMessage
            this.showLoader = true;
            this.getMessageTemplates(() => {
                const found = this.messageTemplates.find(elem => elem.ObjectID === value);
                if (found) {
                    this.workflow.WorkFlowStages.forEach(wfStage => {
                        const messageTemplate = wfStage.WorkFlowMessages.find(wfMessage => wfMessage.ObjectID ===
                            this.selectedMessageTemplate);
                        if (messageTemplate) {
                            messageTemplate.MessageTemplate = found;
                        }
                    });
                }
                this.selectedMessageTemplate = null;
                this.selectedTemplateID = null;
                this.showLoader = false;
            });
        }
        this.displayNewMessageTemplate = false;
    }

    populateMessageTemplates(template: MessageTemplateModel): void {
        this.messageTemplates.splice(this.messageTemplates.length - 1, 1);
        this.messageTemplates.push(template);
    }

    availableStepTypes(): Array<StepTypeEnum> {
        const availableStepTypes = this.stepTypeOptions;
        if (this.workflow.WorkFlowStages) {
            this.workflow.WorkFlowStages.forEach(stage => {
                if (stage.Steps) {
                    stage.Steps.forEach(step => {
                        if (availableStepTypes.indexOf(step.StepType) > -1) {
                            availableStepTypes.slice(availableStepTypes.indexOf(step.StepType), 0);
                        }
                    });
                }
            });
        }

        return availableStepTypes;
    }

    stageMessagesCount(stage: WorkflowStageModel): string {
        return `Message Schedule (${(stage && stage.WorkFlowMessages) ? stage.WorkFlowMessages.length : 0})`;
    }

    addStep($event, stage: WorkflowStageModel): void {

        if (!$event.data[0] && $event.data[0].Value !== null) {
            return;
        }

        const selectedStepType = this.stepTypeOptions.find(step => step.Value === $event.data[0].Value);
        const step = new WorkflowStepModel(stage.Steps.length + 1, selectedStepType, stage.ObjectID);
        this.utilsService.addMultiRefObject(step, stage, 'Steps', this.createdObjs);

        this.removeStepType(selectedStepType);
    }

    removeStep(step: WorkflowStepModel, stage: WorkflowStageModel): void {
        const stepType = step.StepType;
        this.utilsService.removeMultiRefObject(step, stage, 'Steps', this.createdObjs, this.updatedObjs, this.deletedObjs);
        this.reAssignStepOrders(stage);
        this.addStepType(stepType);
    }

    removeStepType(step: StepTypeEnum): void {
        this.stepTypes.splice(this.stepTypes.findIndex(elem => elem.Value === step.Value), 1);
        this.resetSelect('stepTypes');
    }

    addStepType(step: StepTypeEnum): void {
        this.stepTypes.push(step);
        this.resetSelect('stepTypes');
    }

    reAssignStepOrders(stage: WorkflowStageModel): void {
        stage.Steps.map((step, index) => step.SortOrder = index + 1);
    }

    addMessage(stage: WorkflowStageModel): void {
        const message = new WorkflowMessageModel(stage.ObjectID);
        this.utilsService.addMultiRefObject(message, stage, 'WorkFlowMessages', this.createdObjs);
    }

    removeMessage(message: WorkflowMessageModel, stage: WorkflowStageModel): void {
        this.confirmationService.confirm({
            message: 'Are you sure you want to delete this record?',
            header: 'Delete Confirmation',
            icon: 'fa fa-trash',
            key: 'RemoveRow',
            accept: () => {
                this.utilsService.removeMultiRefObject(message, stage, 'WorkFlowMessages', this.createdObjs,
                    this.updatedObjs, this.deletedObjs);
            }
        });
    }

    addStage(): void {
        const stageType = this.stageTypes.find(type => !type.IsPreStage && !type.IsPostStage);
        const stage = new WorkflowStageModel(`${stageType.Description} Stage`, false, stageType, this.getStageOrder(),
            false, this.workflow.ObjectID);
        this.utilsService.addMultiRefObject(stage, this.workflow, 'WorkFlowStages', this.createdObjs);
        this.filterStages(false, true, false);
    }

    removeStage(stage: WorkflowStageModel): void {
        this.confirmationService.confirm({
            message: 'Are you sure you want to delete this record?',
            header: 'Delete Confirmation',
            icon: 'fa fa-trash',
            key: 'RemoveRow',
            accept: () => {
                if (this.isGeneric(stage)) {
                    const decrement = this.workflow.WorkFlowStages.filter(stg => this.isGeneric(stg))
                        .length - stage.SortOrder;
                    if (decrement > 0) {
                        this.workflow.WorkFlowStages.map(stg => {
                            if (this.isGeneric(stg) && stg.SortOrder > decrement) {
                                stg.SortOrder = stg.SortOrder - decrement;
                            }
                        });
                    }
                    this.reOrderStages(this.filterStages(false, true, false));
                }
                this.utilsService.removeMultiRefObject(stage, this.workflow, 'WorkFlowStages', this.createdObjs,
                    this.updatedObjs, this.deletedObjs);
            }
        });
    }

    getStageOrder(): number {
        return this.workflow && this.workflow.WorkFlowStages ? (
            this.workflow.WorkFlowStages.filter(stage => this.isGeneric(stage))
                .length + 1
        ) : 0;
    }

    resetSelect(key: string): void {
        const temp = this[key];
        this[key] = [];
        setTimeout(() => (this[key] = temp), 0);
    }

    getWorkflowTemplateByID(id): void {
        this.showLoader = true;
        this.createdObjs = {};
        this.updatedObjs = {};
        this.deletedObjs = {};

        this.utilsService.resetCounter();
        this.utilsService.clearErrorMessages();

        const assoc = [ASSOCS.WORKFLOW_STAGE, [ASSOCS.WORKFLOW_STAGE, ASSOCS.WORKFLOW_MESSAGE].join('.'),
            [ASSOCS.WORKFLOW_STAGE, ASSOCS.WORKFLOW_MESSAGE, ASSOCS.WORKFLOW_STEP].join('.')];

        this.subscriptions.push(this.editWorkflowTemplateService.getWorkflowTemplateByID(id, assoc)
            .subscribe(
                data => {
                    this.workflow = data;
                    this.updatedObjs[this.workflow.ObjectID] = this.workflow;
                    this.utilsService.addObjsToJSONByObjectID(this.updatedObjs, this.workflow.WorkFlowStages);
                    this.workflow.WorkFlowStages.forEach(stage => {
                        if (stage.WorkFlowMessages) {
                            this.utilsService.addObjsToJSONByObjectID(this.updatedObjs, stage.WorkFlowMessages);
                        }
                        if (stage.Steps) {
                            this.utilsService.addObjsToJSONByObjectID(this.updatedObjs, stage.Steps);
                        }
                    });
                    this.reOrderStages(this.filterStages(true, true, true));
                    this.showLogs();
                    this.showLoader = false;
                },
                error => {
                    this.showLoader = false;
                    this.utilsService.handleError(error);
                }
            )
        );
    }

    getMessageClass(message: MessageTemplateModel): string {
        return message.TemplateType ? `ui-icon-${message.TemplateType.Value.toLowerCase()}` :
            'ui-icon-sms';
    }

    saveWorkflowTemplate(saveCopy = false, testing = true): void {
        this.utilsService.clearErrorMessages();
        const service = `svc/${saveCopy ? SERVICES.SAVE_MESSAGE_AS_COPY : SERVICES.SAVE}`;

        if (testing) {
            this.showLogs();

            return;
        }

        if (this.form.invalid) {
            this.utilsService.showAllErrorMessages();
        } else {
            this.showLoader = true;
            this.subscriptions.push(this.saveService.saveObjects(service, this.createdObjs, this.updatedObjs, this.deletedObjs)
                .subscribe(
                    () => {
                        this.utilsService.handleSuccess();
                        this.router.navigate(['/admin/list-workflow-templates']);
                    },
                    error => {
                        this.showLoader = false;
                        this.utilsService.handleError(error);
                    }
                )
            );
        }
    }

    showLogs(): void {
        console.log(this.workflow);
        console.log(this.createdObjs);
        console.log(this.updatedObjs);
        console.log(this.deletedObjs);
    }

    alreadyExists(): boolean {
        return Object.keys(this.createdObjs).length > 0 || Object.keys(this.updatedObjs).length > 0;
    }

// tslint:disable-next-line:max-file-line-count
}
