import { AfterViewInit, Component, ElementRef, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { DetailsFormBase } from '@objects-view/details-form-base';
import { ITicketModel } from '@fleio-api/plugins/tickets/model/ticket.model';
import { AbstractControl, UntypedFormBuilder, Validators } from '@angular/forms';
import { ITicketCreateOptionsModel } from '@fleio-api/plugins/tickets/model/ticket-create-options.model';
import { ActivatedRoute, Router } from '@angular/router';
import { forkJoin, Observable, of } from 'rxjs';
import { debounceTime, map, startWith, switchMap } from 'rxjs/operators';
import { TicketDepartmentsApiService } from '@fleio-api/plugins/tickets/ticket-departments-api.service';
import { ClientsApiService } from '@fleio-api/client-user/client/clients-api.service';
import { UsersApiService } from '@fleio-api/client-user/user/users-api.service';
import { IUserModel } from '@fleio-api/client-user/model/user.model';
import { ConfigService } from '@shared/config/config.service';
import { TicketsApiService } from '@fleio-api/plugins/tickets/tickets-api.service';
import { UploadInterfaceComponent } from '@shared/ui/upload-interface/upload-interface.component';
import { HttpResponse } from '@angular/common/http';
import { ITicketAttachmentModel } from '@fleio-api/plugins/tickets/model/ticket-attachment.model';
import { TicketAttachmentsApiService } from '@fleio-api/plugins/tickets/ticket-attachments-api.service';
import { AppLocalStorageService } from '@shared/ui-api/app-local-storage.service';
import { ValidatorsHelper } from '@shared/ui-api/helpers/validators-helper';
import {
  InsertPredefinedReplyDialogComponent
} from '@shared/plugins/common-dialogs/tickets/insert-predefined-reply-dialog/insert-predefined-reply-dialog.component';
import { IActionResult } from '@objects-view/interfaces/actions/action-result';
import { MatDialog } from '@angular/material/dialog';
import { EditorComponent } from '@tinymce/tinymce-angular';
import { ThemingService } from '@shared/ui/theming/theming.service';
import { TinyMCEAttachments, TinymceHelper } from '@shared/ui-api/helpers/tinymce-helper';
import { COMMA, ENTER, SEMICOLON, SPACE } from '@angular/cdk/keycodes';
import { MatAutocompleteSelectedEvent, MatAutocompleteTrigger } from '@angular/material/autocomplete';
import { NotificationService } from '@shared/ui-api/notification.service';
import { IIcon } from '@shared/ui/common/interfaces/icon';
import { AutocompleteHelper } from '@shared/ui-api/helpers/autocomplete-helper';

let TINYMCE_ATTACHMENTS: TinyMCEAttachments;

@Component({
  selector: 'app-tickets-open-new-tab',
  templateUrl: './tickets-open-new-tab.component.html',
  styleUrls: ['./tickets-open-new-tab.component.scss']
})
export class TicketsOpenNewTabComponent extends DetailsFormBase<ITicketModel>
  implements OnInit, OnDestroy, AfterViewInit {

  @ViewChild('uploadInterface') uploadInterface: UploadInterfaceComponent;
  @ViewChild('tinyMceArea') tinyMceArea: EditorComponent;
  @ViewChild('toChipInput') toChipInput: ElementRef;
  @ViewChild('ccChipInput') ccChipInput: ElementRef;
  @ViewChild('toTrigger') toTrigger: MatAutocompleteTrigger;
  tinyMceHelper = new TinymceHelper();
  openTicketForm = this.formBuilder.group({
    title: ['', Validators.required],
    priority: ['medium', Validators.required],
    department: ['', [Validators.required, ValidatorsHelper.requiredObjectValidator]],
    status: ['open', Validators.required],
    cc_recipients: [null],
    cc_recipients_input: [''],
    client: ['', ValidatorsHelper.objectOrEmptyValidator],
    to_recipients: [null, Validators.required],
    to_recipients_input: [''],
    description: [''],
    assigned_to: [''],
  });

  filteredToRecipients$: Observable<Array<IUserModel>>;
  filteredCCRecipients$: Observable<Array<IUserModel>>;
  createOptions: ITicketCreateOptionsModel;
  prioritiesKeys: Array<string>;
  statusesKeys: Array<string>;
  tinyMCEOptions = null;
  separatorKeysCodes: number[] = [ENTER, COMMA, SEMICOLON, SPACE];
  toRecipients = this.openTicketForm.controls.to_recipients;
  ccRecipients = this.openTicketForm.controls.cc_recipients;
  clientWithRecipientsSelected = false;

  departmentSelectedPlaceholder = $localize`Department`;
  departmentUnselectedPlaceholder = $localize`Select department ...`;
  assignedToSelectedPlaceholder = $localize`Assigned to`;
  assignedToUnselectedPlaceholder = $localize`Select assignee`;

  clientInfoIcon: IIcon = {
    name: 'info',
    verticalAlign: 'middle',
    tooltip: $localize`If a client is selected, the To field is hidden\nand recipients are selected automatically.`,
  };
  toRecipientsInfoIcon: IIcon = {
    name: 'info',
    verticalAlign: 'middle',
    tooltip: $localize`Select user or manually fill in an email address.\nUse this field when no client is selected.`,
  };
  ccRecipientsInfoIcon: IIcon = {
    name: 'info',
    verticalAlign: 'middle',
    tooltip: $localize`Select user or manually fill in an email address.`,
  };

  constructor(private formBuilder: UntypedFormBuilder, private activatedRoute: ActivatedRoute,
              private ticketsApiService: TicketsApiService, private router: Router,
              public ticketDepartmentsApiService: TicketDepartmentsApiService,
              private clientsApiService: ClientsApiService, private usersApiService: UsersApiService,
              private config: ConfigService, private ticketAttachmentsApiService: TicketAttachmentsApiService,
              private appLocalStorageService: AppLocalStorageService, private matDialog: MatDialog,
              private themingService: ThemingService, private notificationService: NotificationService) {
    super();
    TINYMCE_ATTACHMENTS = new TinyMCEAttachments(this.ticketAttachmentsApiService);
  }

  getRecipientDisplay(recipient) {
    if (typeof recipient === 'string') {
      return recipient;
    } else {
      if (recipient.full_name) {
        return $localize`${recipient.full_name} (${recipient.email})`;
      } else {
        return recipient.email;
      }
    }
  }

  removeRecipient(recipientToRemove, formControl: AbstractControl) {
    let recipients = formControl.value as Array<string>;
    recipients = recipients.filter(recipient => recipient !== recipientToRemove);
    formControl.setValue(recipients);
    AutocompleteHelper.refreshAutocompleteOptions(this.openTicketForm.controls.to_recipients_input);
    AutocompleteHelper.refreshAutocompleteOptions(this.openTicketForm.controls.cc_recipients_input);
  }

  private addRecipient(recipient, formControl: AbstractControl) {
    const recipients = formControl.value as Array<string>;
    if (recipients) {
      recipients.push(recipient);
      formControl.setValue(recipients);
    } else {
      formControl.setValue([recipient]);
    }
  }

  addCustomToRecipient(event) {
    const recipientValue = event.value as string;
    if (!recipientValue.includes('@')) {
      // list will be better validated on backend
      this.notificationService.showMessage($localize`Recipients should be email addresses!`);
    } else if (this.findToRecipient(recipientValue)) {
      this.notificationService.showMessage($localize`${recipientValue} already in list!`);
    } else {
      this.addRecipient(recipientValue, this.toRecipients);
      if (event.chipInput) {
        event.chipInput.clear();
      }
      this.openTicketForm.controls.to_recipients_input.setValue('');
      this.openTicketForm.controls.cc_recipients_input.setValue('');
    }
  }

  addRecipientOnFocusOut(chipInput, recipients, cc = false) {
    const inputValue = chipInput.value;
    if (inputValue && inputValue.includes('@')) {
      if (cc && this.findToRecipient(inputValue)) {
        // do not auto add CC email if already found in To recipients
        return;
      }
      this.addRecipient(inputValue, recipients);
      chipInput.value = '';
    }
  }

  private findToRecipient(recipientValue: string) {
    if (this.toRecipients.value) {
      return this.toRecipients.value.find(recipient => {
        if (typeof recipient === 'string') {
          return recipient === recipientValue;
        } else {
          return recipient.email === recipientValue;
        }
      });
    } else {
      return null;
    }
  }

  addCustomCCRecipient(event) {
    const recipientValue = event.value as string;
    if (!recipientValue.includes('@')) {
      // list will be better validated on backend
      this.notificationService.showMessage($localize`Recipients should be email addresses!`);
    } else if (this.findToRecipient(recipientValue)) {
      this.notificationService.showMessage($localize`Recipient ${recipientValue} is already automatically emailed!`);
    } else {
      this.addRecipient(recipientValue, this.ccRecipients);
      if (event.chipInput) {
        event.chipInput.clear();
      }
      this.openTicketForm.controls.cc_recipients_input.setValue('');
    }
  }

  toRecipientSelected(event: MatAutocompleteSelectedEvent) {
    const recipient = event.option.value;
    this.addRecipient(recipient, this.toRecipients);
    this.toChipInput.nativeElement.value = '';
    this.openTicketForm.controls.to_recipients_input.setValue('');
    this.openTicketForm.controls.cc_recipients_input.setValue('');
  }

  ccRecipientSelected(event: MatAutocompleteSelectedEvent) {
    const recipient = event.option.value;
    this.addRecipient(recipient, this.ccRecipients);
    this.ccChipInput.nativeElement.value = '';
    this.openTicketForm.controls.cc_recipients_input.setValue('');
  }

  selectedClient(client) {
    this.getRecipientsForClient(client.id);
  }

  getRecipientsForClient(clientID) {
    if (clientID) {
      this.ticketsApiService.getClientRecipients(clientID).subscribe((users: Array<IUserModel>) => {
        if (users && users.length) {
          this.toRecipients.setValue(users);
          this.toRecipients.disable();
          this.openTicketForm.controls.to_recipients_input.disable();
          this.clientWithRecipientsSelected = true;
        } else {
          this.toRecipients.setValue(null);
          if (this.toRecipients.disabled) {
            this.toRecipients.enable();
          }
          if (this.openTicketForm.controls.to_recipients_input.disabled) {
            this.openTicketForm.controls.to_recipients_input.enable();
          }
          this.clientWithRecipientsSelected = false;
        }
      });
    }
  }

  openTicket() {
    const value = this.openTicketForm.value;
    const fileInputs = this.uploadInterface.getFileInputs();
    const allFiles = [];
    for (const fileInput of fileInputs) {
      if (fileInput.nativeElement.files.length) {
        // on ticket reply, for one input there is at most one file
        allFiles.push(fileInput.nativeElement.files[0]);
      }
    }
    if (allFiles.length) {
      let fd: FormData;
      const queriesList = [];
      for (const file of allFiles) {
        fd = new FormData();
        fd.append('data', file);
        fd.append('file_name', file.name);
        queriesList.push(this.ticketAttachmentsApiService.createWithUpload(fd));
      }
      forkJoin(queriesList).subscribe({
        next: responses => {
          let attachmentIDs = '';
          responses.forEach((queryResponse: HttpResponse<ITicketAttachmentModel>, index) => {
            attachmentIDs += queryResponse.body.id;
            if (index < responses.length - 1) {
              attachmentIDs += ',';
            }
          });
          this.tinyMceArea.editor.uploadImages().then(() => {
            this.addTicket(value, attachmentIDs, true);
          });
          return of(null);
        }, error: error => {
          if (error?.error?.detail) {
            this.notificationService.showMessage(error.error.detail);
          }
          this.objectController.loading = false;
        }
      });
      return of(null);
    } else {
      this.tinyMceArea.editor.uploadImages().then(() => {
        this.addTicket(value, '');
      });
      return of(null);
    }
  }

  openInsertPredefinedReplyDialog() {
    return this.matDialog.open(
      InsertPredefinedReplyDialogComponent, {}).afterClosed().subscribe(result => {
      if (!result) {
        return;
      }
      this.tinyMceArea.editor.execCommand('mceInsertContent', false, result);
      return {message: result} as IActionResult;
    });
  }

  private parseRecipients(recipients: Array<any>, joinEmails = false) {
    const parsedRecipients = [];
    recipients.forEach(recipient => {
      if (typeof recipient === 'string') {
        parsedRecipients.push(recipient);
      } else {
        parsedRecipients.push(recipient.email);
      }
    });
    if (parsedRecipients.length) {
      if (joinEmails) {
        return parsedRecipients.join(',');
      } else {
        return JSON.stringify(parsedRecipients);
      }
    } else {
      return null;
    }
  }

  addTicket(value, associatedAttachmentsIds: string, resetFileInputs = false) {
    associatedAttachmentsIds = TINYMCE_ATTACHMENTS.appendAttachments(associatedAttachmentsIds);
    value.description = this.tinyMceArea.editor.getContent();
    if (value.department) {
      value.department = value.department.id;
    }
    if (value.client) {
      value.client = value.client.id;
    }
    if (value.assigned_to) {
      value.assigned_to = value.assigned_to.id;
    }

    if (value.to_recipients) {
      // parse "TO" recipients before submitting
      value.to_recipients = this.parseRecipients(value.to_recipients);
    }

    if (value.cc_recipients) {
      // parse "CC" recipients before submitting
      value.cc_recipients = this.parseRecipients(value.cc_recipients, true);
    }

    if (associatedAttachmentsIds) {
      value.associated_attachment_ids = associatedAttachmentsIds;
    }
    this.createOrUpdate(this.ticketsApiService, value).subscribe((response: any) => {
      if (resetFileInputs) {
        this.uploadInterface.resetFileInputs();
      }
      this.appLocalStorageService.setTicketOpenData(null);
      this.router.navigateByUrl(
        this.config.getPanelUrl(`tickets/${response.id}`)
      ).catch(() => {
      });
    });
  }

  tinymceUploadHandler(blobInfo, success, failure, progress) {
    const attachmentIdentifier = `${blobInfo.id()}${new Date().getTime()}`;
    const fd = new FormData();
    fd.append('data', blobInfo.blob());
    fd.append('file_name', blobInfo.filename());
    fd.append('custom_id', attachmentIdentifier);
    return TINYMCE_ATTACHMENTS.attachmentsApiService.createWithUpload(fd).toPromise().then((resp) => {
      if (resp?.body?.id) {
        TINYMCE_ATTACHMENTS.attachmentsIDs.push(resp.body.id);
        TINYMCE_ATTACHMENTS.blobsToReplace[blobInfo.blobUri()] = attachmentIdentifier;
        return `cid:${attachmentIdentifier}`;
      }
    });
  }

  ngOnInit(): void {
    super.ngOnInit();
    this.createOptions = this.activatedRoute.snapshot.data.createOptions;
    if (this.createOptions) {
      this.prioritiesKeys = Object.keys(this.createOptions.priorities);
      this.statusesKeys = Object.keys(this.createOptions.statuses);
    }
    if (this.objectController) {
      this.objectController.actionCallback = () => this.openTicket();
    }
    if (this.config.current) {
      this.tinyMCEOptions = this.themingService.getTinyMceOptions(this.tinymceUploadHandler);
    }
    this.openTicketForm.valueChanges.subscribe(
      ticketForm => {
        this.appLocalStorageService.setTicketOpenData(ticketForm);
      }
    );
    const temporarySavedCreateForm = this.appLocalStorageService.getTicketOpenData();
    if (temporarySavedCreateForm) {
      this.openTicketForm.patchValue(temporarySavedCreateForm);
      if (temporarySavedCreateForm.client) {
        this.getRecipientsForClient(temporarySavedCreateForm.client.id);
        this.toRecipients.disable();
        this.openTicketForm.controls.to_recipients_input.disable();
        this.openTicketForm.controls.cc_recipients_input.setValue('');
      }
      if (temporarySavedCreateForm?.department?.id) {
        this.ticketDepartmentsApiService.get(
          temporarySavedCreateForm.department.id, {}, true
        ).subscribe({
          error: error => {
            if (error?.status === 404) {
              this.openTicketForm.controls.department.setValue('');
            }
          }
        });
      }
    }
    if (!this.openTicketForm.controls.description.value) {
      if (this.createOptions && this.createOptions.user_signature) {
        this.openTicketForm.controls.description.setValue('<br>' + this.createOptions.user_signature);
      }
    }

    this.activatedRoute.params.subscribe(value => {
      if (value && value.forclient) {
        this.clientsApiService.get(value.forclient).subscribe(response => {
          this.openTicketForm.controls.client.setValue(response);
        });
      }
    });

    this.openTicketForm.controls.client.valueChanges.subscribe(value => {
      if (!value || typeof value === 'string') {
        this.clientWithRecipientsSelected = false;
        this.toRecipients.setValue(null);
        if (this.toRecipients.disabled) {
          this.toRecipients.enable();
        }
        if (this.openTicketForm.controls.to_recipients_input.disabled) {
          this.openTicketForm.controls.to_recipients_input.enable();
        }
      } else {
        this.selectedClient(value);
        // this will trigger a CC options refresh after client select
        this.openTicketForm.controls.cc_recipients_input.setValue('');
      }
    });

    this.filteredToRecipients$ = this.openTicketForm.controls.to_recipients_input.valueChanges.pipe(
      startWith(''),
      debounceTime(300),
      switchMap(value => {
        let filtering = 'is_staff:false+is_active:true';
        if (this.toRecipients.value && Array.isArray(this.toRecipients.value)) {
          this.toRecipients.value.forEach(recipient => {
            let recipientValue: string;
            if (typeof recipient === 'string') {
              recipientValue = recipient;
            } else {
              recipientValue = recipient.email;
            }
            recipientValue = recipientValue.replace('+', '\\+');
            filtering += `+email__ne:${recipientValue}`;
          });
        }
        return this.usersApiService.list({
          search: value,
          filtering,
        }).pipe(
          map(response => response.objects)
        );
      }),
    );

    this.filteredCCRecipients$ = this.openTicketForm.controls.cc_recipients_input.valueChanges.pipe(
      startWith(''),
      debounceTime(300),
      switchMap(value => {
        let filtering = 'is_staff:false+is_active:true';
        if (this.toRecipients.value && Array.isArray(this.toRecipients.value)) {
          this.toRecipients.value.forEach(recipient => {
            let recipientValue: string;
            if (typeof recipient === 'string') {
              recipientValue = recipient;
            } else {
              recipientValue = recipient.email;
            }
            recipientValue = recipientValue.replace('+', '\\+');
            filtering += `+email__ne:${recipientValue}`;
          });
        }
        if (this.ccRecipients.value && Array.isArray(this.ccRecipients.value)) {
          this.ccRecipients.value.forEach(recipient => {
            let recipientValue: string;
            if (typeof recipient === 'string') {
              recipientValue = recipient;
            } else {
              recipientValue = recipient.email;
            }
            recipientValue = recipientValue.replace('+', '\\+');
            filtering += `+email__ne:${recipientValue}`;
          });
        }
        if (this.openTicketForm.controls.client.value?.id) {
          filtering += `+clients__id:${this.openTicketForm.controls.client.value.id}`;
        }
        return this.usersApiService.list({
          search: value,
          filtering,
        }).pipe(
          map(response => response.objects)
        );
      }),
    );
  }

  ngAfterViewInit() {
    super.ngAfterViewInit();
    this.tinyMceHelper.manageUndo(this.openTicketForm.controls.description, this.tinyMceArea);
  }

  handleTinyKeyDown(event) {
    if (event && event.event && event.event.ctrlKey && event.event.keyCode === 13) {
      event.event.preventDefault();
      this.openTicket();
    }
  }

  ngOnDestroy() {
    super.ngOnDestroy();
    this.tinyMceHelper.removeSubscriptionForUndo();
  }

}
