import { Component, OnInit, ViewChild } from '@angular/core';
import { NgForm } from '@angular/forms';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { DndDropEvent } from 'ngx-drag-drop';
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 { ApplicantSourcesEnum } from '../../../models/applicant-sources.enum';
import { MessageTemplateModel } from '../../../models/message-template.model';
import { ResponseActionEnum } from '../../../models/response-action.enum';
import { StageTypeEnum } from '../../../models/stage-type.enum';
import { StepTypeEnum } from '../../../models/step-type.enum';
import { WorkflowMessageModel } from '../../../models/workflow-message.model';
import { WorkflowStageModel } from '../../../models/workflow-stage.model';
import { WorkflowStepModel } from '../../../models/workflow-step.model';
import { WorkflowTemplateModel } from '../../../models/workflow-template.model';
import { SaveService } from '../../../oneit/services/save.service';
import { UtilsService } from '../../../oneit/services/utils.service';
import { AdminPortalLayoutService } from '../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>;
    applicantSources: Array<ApplicantSourcesEnum>;
    messageTemplates: Array<MessageTemplateModel> = [];
    editField: any = {};
    showLoader = false;
    editMode = false;
    displayNewMessageTemplate = false;
    draggingItem: any = null;
    draggingId = null;
    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 {
        window.scrollTo(0, 0);
        this.showLoader = true;
        if (this.adminPortalLayoutService.userData) {
            this.initComponent();
        } else {
            this.subscriptions.push(this.adminPortalLayoutService.userDataUpdated
                .first()
                .subscribe(() => {
                    this.initComponent();
                }));
        }
    }

    initComponent(): void {
        this.subscriptions.push(this.activatedRoute.params
            .subscribe((params: Params) => {
                if (params.id && params.id !== '0') {
                    this.editMode = true;
                }

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

                            this.stageTypes = response[0];
                            this.stepTypeOptions = response[1];
                            // Changed null value to 'null' in order to use it in select-2 component which takes value as a string
                            this.stepTypeOptions.filter(type => type.Value === null)
                                .map(val => {
                                    val.Value = 'null';

                                    return val;
                                });

                            // Diversity is shown only is enabled by selected hiring team
                            if (!this.adminPortalLayoutService.getHiringTeamID().HasDiversity) {
                                this.stepTypeOptions = this.stepTypeOptions.filter(type => type.Value !== 'DIVERSITY');
                            }

                            this.responseActions = response[2];
                            this.applicantSources = response[3];

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

    /**
     * When a new template is created, following default values are to assigned.
     */
    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;
    }

    /**
     * Other than generic, add pre-stage and post-stage
     */
    addDefaultStages(): void {
        this.workflow.WorkFlowStages = [];
        this.stageTypes.forEach(stage => {

            if (!stage.IsPreStage && !stage.IsPostStage) {
                return;
            }

            const index = -1;
            const stageObject = new WorkflowStageModel(`${stage.Description} Stage`, false, stage, index,
                false, this.workflow.ObjectID);
            this.utilsService.addMultiRefObject(stageObject, this.workflow, 'WorkFlowStages', this.createdObjs);
            const withdrawalMessage = new WorkflowMessageModel(stageObject.ObjectID);
            withdrawalMessage.IsWithdrawalMessage = true;
            this.utilsService.addMultiRefObject(withdrawalMessage, stageObject, 'WorkFlowMessages', this.createdObjs);
        });
        this.reOrderStages();
    }

    reOrderStages(): void {
        this.workflow.WorkFlowStages = this.utilsService.sort(this.workflow.WorkFlowStages, ['SortOrder'], [1]);
        this.filterStages();
    }

    reOrderSteps(): void {
        this.workflow.WorkFlowStages.forEach(stage => {
            stage.Steps = this.utilsService.sort(stage.Steps, ['SortOrder'], [1]);
        });
    }

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

    /**
     * Instead of filtering stages on UI, every time a single change is made.
     * This function creates three separate arrays for three types of stages (pre-stage, generic-stage, post-stage)
     */
    filterStages(): void {
        this.preStages = this.workflow.WorkFlowStages.filter(stage => stage.StageType.IsPreStage);

        this.genericStages = this.workflow.WorkFlowStages.filter(stage => !stage.StageType.IsPreStage && !stage.StageType.IsPostStage);

        this.postStages = this.workflow.WorkFlowStages.filter(stage => stage.StageType.IsPostStage);
    }

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

    showSaveCopyBtn(): boolean {
        if (this.workflow && this.workflow.ObjectID) {
            return this.workflow.HiringTeam === null;
        }

        return false;
    }

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

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

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

    sortWorkFlowMessage(): void {
        if (this.workflow && this.workflow.WorkFlowStages) {
            this.workflow.WorkFlowStages.forEach(stage => {
                if (stage && stage.WorkFlowMessages) {
                    stage.WorkFlowMessages.sort((a, b) => a.DelayMinutes - b.DelayMinutes);
                }
            });
        }
    }

    toggleInput(event, fieldName, value): void {
        this.utilsService.clearErrorMessages();
        if (value === false) {
            if (this.form.controls[fieldName]) {
                if (!this.form.controls[fieldName].invalid) {
                    this.editField[fieldName] = false;
                } else {
                    this.utilsService.showAllErrorMessages();
                }
            } else {
                if (event.target.value !== '') {
                    this.editField[fieldName] = false;
                }
            }
        } 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);
        }
    }

    generateVarianceString(message: WorkflowMessageModel): void {

        if (!message.Variance) {
            return;
        }
        message.VarianceString = `${message.Variance}m`;
    }

    /**
     * Whenever Delay value is provided in message schedule section, it changes it into a delay string consisting of
     * number of days, hours and minutes.
     * Input mask gets values in the form of __:__:__
     * Parsing of this value takes place in this function, moreover it sorts the messages as well
     * @param message
     */
    generateDelayString(message: WorkflowMessageModel): void {

        if (!message.Delay) {
            return;
        }

        const delay = message.Delay.replace(/_/g, '')
            .split(':');

        delay[0] = delay[0] || '';
        delay[1] = delay[1] || '';
        delay[2] = delay[2] || '';

        if (delay[1] === '' && delay[2] === '') {
            message.DelayString = `${delay[0]}m`;
            message.DelayMinutes = parseInt(delay[0], 10);
        } else if (delay[2] === '') {
            message.DelayString = `${delay[0]}h: ${delay[1]}m`;
            message.DelayMinutes = parseInt(delay[0], 10) * 60 + parseInt(delay[1], 10);
        } else {
            message.DelayString = `${delay[0]}d: ${delay[1]}h: ${delay[2]}m`;
            message.DelayMinutes = parseInt(delay[0], 10) * 24 * 60 + parseInt(delay[1], 10) * 60 + parseInt(delay[1], 10);
        }

        this.sortWorkFlowMessage();
    }

    getMessageTemplates(callback = null): void {
        const sharedMsgParams = {
            OnlyGlobalTemplate: true,
            attribsByName: 'Autocomplete'
        };
        const myMsgParams = {
            OnlyGlobalTemplate: false,
            attribsByName: 'Autocomplete',
            HiringTeam: this.adminPortalLayoutService.getHiringTeamID().ObjectID
        };

        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();
                        this.assignMessageTemplate();
                        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);
    }

    /**
     * This function sets the parameters to be sent to edit-message-template component
     * @param $event
     * @param message
     */
    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.selectedMessageTemplate = message.ObjectID;
            this.displayNewMessageTemplate = true;
        }
    }

    /**
     * When the dialog is closed, whether a template is created or updated, it is fetched by sending request to API.
     * Newly created or updated template is selected and shown in the dropdown of message in message scheduling section.
     * @param value
     */
    closeDialog(value): void {
        if (value === -1) {
            // Cancel button is clicked
        } else if (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;
    }

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

    addStep($event, stage: WorkflowStageModel): void {
        const selectedStepType = this.stepTypeOptions.find(step => step.Value === $event.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 {
        this.confirmationService.confirm({
            message: 'Are you sure you want to delete this record?',
            header: 'Delete Confirmation',
            icon: 'fa fa-trash',
            key: 'RemoveRow',
            accept: () => {
                const stepType = step.StepType;

                this.utilsService.removeMultiRefObject(step, stage, 'Steps', this.createdObjs, this.updatedObjs, this.deletedObjs);
                stage.Steps.map((step, index) => step.SortOrder = index + 1);
                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');
    }

    addMessage(stage: WorkflowStageModel): void {
        const message = new WorkflowMessageModel(stage.ObjectID);
        message.ApplicantSources = this.utilsService.cloneObject(this.applicantSources);
        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);
        const withdrawalMessage = new WorkflowMessageModel(stage.ObjectID);
        withdrawalMessage.IsWithdrawalMessage = true;
        this.utilsService.addMultiRefObject(withdrawalMessage, stage, 'WorkFlowMessages', this.createdObjs);
        this.reOrderStages();
    }

    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: () => {
                this.utilsService.removeMultiRefObject(stage, this.workflow, 'WorkFlowStages', this.createdObjs,
                    this.updatedObjs, this.deletedObjs);
                this.workflow.WorkFlowStages.forEach((stag, index) => stag.SortOrder = index + 1);
                this.filterStages();
            }
        });
    }

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

    resetSelect(key: string): void {
        this[key] = [...this[key]];
    }

    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, ASSOCS.MESSAGE_TEMPLATE].join('.'),
            [ASSOCS.WORKFLOW_STAGE, ASSOCS.WORKFLOW_MESSAGE].join('.'), [ASSOCS.WORKFLOW_STAGE, 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 = this.workflow.WorkFlowStages || [];
                    this.workflow.WorkFlowStages.forEach(stage => {

                        stage.Steps = stage.Steps || [];
                        stage.WorkFlowMessages = stage.WorkFlowMessages || [];

                        stage.WorkFlowMessages.forEach(msg => {
                            this.generateDelayString(msg);
                            this.generateVarianceString(msg);
                        });

                        stage.Steps.forEach(step => {
                            this.removeStepType(step.StepType);
                        });

                        this.utilsService.addObjsToJSONByObjectID(this.updatedObjs, stage.WorkFlowMessages);
                        this.utilsService.addObjsToJSONByObjectID(this.updatedObjs, stage.Steps);
                    });
                    this.assignMessageTemplate();
                    this.reOrderStages();
                    this.reOrderSteps();
                    this.showLoader = false;
                },
                error => {
                    this.showLoader = false;
                    this.utilsService.handleError(error);
                }
            )
        );
    }

    assignMessageTemplate(): void {

        if (!this.workflow || !this.workflow.WorkFlowStages || !this.messageTemplates) {
            return;
        }

        this.workflow.WorkFlowStages.forEach(stage => {

            stage.WorkFlowMessages.forEach(msg => {
                if (msg.MessageTemplate && msg.MessageTemplate.ObjectID) {
                    msg.MessageTemplate = this.messageTemplates.find(elem => elem.ObjectID === msg.MessageTemplate.ObjectID);
                }
            });
        });
    }

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

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

        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);
                    }
                )
            );
        }
    }

    onDragStart(item, list, listId): void {
        this.draggingItem = item;
        this.draggingId = listId;
    }

    onDragEnd(event): void {
        setTimeout(() => {
            this.draggingItem = null;
            this.draggingId = null;
        });
    }

    onDrop(event: DndDropEvent, list?: Array<any>, draggingId?: any): void {
        if (draggingId !== this.draggingId) {
            return;
        }
        let index = event.index;

        if (typeof index === 'undefined') {

            index = list.length;
        }

        if (this.draggingItem) {
            const oldIndex = list.findIndex(item => item.ObjectID === this.draggingItem.ObjectID);
            if (oldIndex < index) {
                index -= 1;
            }

            list.splice(oldIndex, 1);
            list.splice(index, 0, this.draggingItem);
        }

        list.forEach((item, i) => {
            item.SortOrder = i + 1;
        });
    }

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