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 { IUserModel } from '@fleio-api/client-user/model/user.model';
import { ITicketCreateOptionsModel } from '@fleio-api/plugins/tickets/model/ticket-create-options.model';
import { ActivatedRoute, Router } from '@angular/router';
import { TicketsApiService } from '@fleio-api/plugins/tickets/tickets-api.service';
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 { ConfigService } from '@shared/config/config.service';
import { Observable, of } from 'rxjs';
import { debounceTime, map, startWith, switchMap } from 'rxjs/operators';
import { FleioId } from '@fleio-api/base-model/base-fleio-object.model';
import { ThemingService } from '@shared/ui/theming/theming.service';
import { EditorComponent } from '@tinymce/tinymce-angular';
import { TinymceHelper } from '@shared/ui-api/helpers/tinymce-helper';
import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { NotificationService } from '@shared/ui-api/notification.service';
import { COMMA, ENTER, SEMICOLON, SPACE } from '@angular/cdk/keycodes';
import { IIcon } from '@shared/ui/common/interfaces/icon';
import { ValidatorsHelper } from '@shared/ui-api/helpers/validators-helper';
import { AutocompleteHelper } from '@shared/ui-api/helpers/autocomplete-helper';

@Component({
  selector: 'app-ticket-edit-form',
  templateUrl: './ticket-edit-form.component.html',
  styleUrls: ['./ticket-edit-form.component.scss']
})
export class TicketEditFormComponent extends DetailsFormBase<ITicketModel>
  implements OnInit, OnDestroy, AfterViewInit {

  @ViewChild('tinyMceArea') tinyMceArea: EditorComponent;
  @ViewChild('toChipInput') toChipInput: ElementRef;
  @ViewChild('ccChipInput') ccChipInput: ElementRef;

  tinyMceHelper = new TinymceHelper();
  editTicketForm = this.formBuilder.group({
    title: ['', Validators.required],
    priority: ['medium', Validators.required],
    department: ['', Validators.required],
    status: ['open', Validators.required],
    to_recipients: [null, Validators.required],
    to_recipients_input:[''],
    cc_recipients: [null],
    cc_recipients_input: [''],
    client: ['', ValidatorsHelper.objectOrEmptyValidator],
    description: [''],
    assigned_to: [''],
  });
  toRecipients = this.editTicketForm.controls.to_recipients;
  ccRecipients = this.editTicketForm.controls.cc_recipients;
  toRecipientsHint = null;

  filteredToRecipients$: Observable<Array<IUserModel>>;
  filteredCCRecipients$: Observable<Array<IUserModel>>;
  createOptions: ITicketCreateOptionsModel;
  prioritiesKeys: Array<string>;
  statusesKeys: Array<string>;
  tinyMCEOptions = null;
  separatorKeysCodes: number[] = [ENTER, COMMA, SEMICOLON, SPACE];
  clientWithRecipientsSelected = false;
  clientInfoIcon: IIcon = {
    name: 'info',
    verticalAlign: 'middle',
    marginLeft: '10px',
    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.`,
  };
  assignedToSelectedPlaceholder = $localize`Assigned to`;
  assignedToUnselectedPlaceholder = $localize`Select assignee`;
  departmentSelectedPlaceholder = $localize`Department`;
  departmentUnselectedPlaceholder = $localize`Select department ...`;

  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 themingService: ThemingService,
              private notificationService: NotificationService,) {
    super();
  }

  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.editTicketForm.controls.to_recipients_input);
    AutocompleteHelper.refreshAutocompleteOptions(this.editTicketForm.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.editTicketForm.controls.to_recipients_input.setValue('');
      this.editTicketForm.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.editTicketForm.controls.cc_recipients_input.setValue('');
    }
  }

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

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

  saveTicket() {
    const value = this.editTicketForm.value;
    this.editTicket(value);
    return of(null);
  }

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

  editTicket(value) {
    if (value.department) {
      value.department = value.department.id;
    }

    if (value.client) {
      value.client = value.client.id;
    }

    if (value.client && this.clientWithRecipientsSelected) {
      value.to_recipients = JSON.stringify([]);
    } else if (value.to_recipients) {
      // parse "TO" recipients before submitting
      value.to_recipients = this.parseRecipients(value.to_recipients);
    }

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

    this.createOrUpdate(this.ticketsApiService, value).subscribe((response: any) => {
      this.router.navigateByUrl(
        this.config.getPanelUrl(`tickets/${response.id}`)
      ).catch(() => {
      });
    });
  }

  preselectDepartment(departmentId: FleioId) {
    this.ticketDepartmentsApiService.get(departmentId).subscribe(value => {
      this.editTicketForm.controls.department.setValue(value);
    });
  }

  preselectClient(clientId: FleioId) {
    this.clientsApiService.get(clientId).subscribe(value => {
      this.editTicketForm.controls.client.setValue(value);
    });
  }

  preselectAssignedTo(userId: FleioId) {
    this.usersApiService.get(userId).subscribe(value => {
      this.editTicketForm.controls.assigned_to.setValue(value);
    });
  }

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

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

  ngOnInit(): void {
    super.ngOnInit();
    if (this.object) {
      if (this.object.cc_recipients) {
        this.object.cc_recipients = (this.object.cc_recipients as string).split(',');
      }
      this.editTicketForm.patchValue(this.object);
      if (this.object.client) {
        this.getRecipientsForClient(this.object.client, false);
      } else {
        if (!this.editTicketForm.controls.to_recipients.value || !this.editTicketForm.controls.to_recipients.value.length) {
          // ticket opened by enduser and without client, populate to_recipients as they are decided when notifying
          // and disable field
          if (this.object.to_recipients_display) {
            const patchedRecipients = [];
            const recipients = this.object.to_recipients_display.split(',');
            recipients.forEach(recipient => {
              patchedRecipients.push(recipient.trim());
            });
            this.editTicketForm.controls.to_recipients.setValue(patchedRecipients);
          }
          this.editTicketForm.controls.to_recipients.disable();
          this.toRecipientsHint = $localize`Ticket has no client associated, recipient is ticket creator.`;
        }
      }
      if (this.object.department) {
        this.preselectDepartment(this.object.department);
      }
      if (this.object.client) {
        this.preselectClient(this.object.client);
      }
      if (this.object.assigned_to) {
        // @ts-ignore
        this.preselectAssignedTo(this.object.assigned_to as FleioId);
      }
    }
    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.saveTicket();
    }
    if (this.config.current) {
      this.tinyMCEOptions = this.themingService.getTinyMceOptions();
    }

    this.editTicketForm.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.editTicketForm.controls.to_recipients_input.disabled) {
          this.editTicketForm.controls.to_recipients_input.enable();
        }
      } else {
        this.selectedClient(value);
        // this will trigger a CC options refresh after client select
        this.editTicketForm.controls.cc_recipients_input.setValue('');
      }
      this.toRecipientsHint = null;
    });

    this.filteredToRecipients$ = this.editTicketForm.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.editTicketForm.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.editTicketForm.controls.client.value?.id) {
          filtering += `+clients__id:${this.editTicketForm.controls.client.value.id}`;
        }
        return this.usersApiService.list({
          search: value,
          filtering,
        }).pipe(
          map(response => response.objects)
        );
      }),
    );
  }

  ngAfterViewInit() {
    super.ngAfterViewInit();
    this.tinyMceHelper.manageUndo(this.editTicketForm.controls.description, this.tinyMceArea);
    if (this.toRecipientsHint) {
      this.toChipInput.nativeElement.placeholder = '';
    }
  }

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

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

}
