import {
  Component,
  OnInit,
  Output,
  EventEmitter,
  ViewChild,
  AfterViewInit,
  ChangeDetectorRef,
  OnDestroy,
  ElementRef,
  Input
} from '@angular/core';

import {
  ucapAnimations,
  DialogService,
  AlertDialogResult,
  AlertDialogComponent,
  AlertDialogData
} from '@ucap-webmessenger/ui';

import { NGXLogger } from 'ngx-logger';

import moment from 'moment';

import { FileUtil } from '@ucap-webmessenger/core';
import { FormGroup, FormBuilder } from '@angular/forms';
import {
  ContentType,
  CategoryType,
  MessageType,
  DetailResponse,
  DetailReceiver,
  DetailContent
} from '@ucap-webmessenger/api-message';
import { FileUploadItem } from '@ucap-webmessenger/api';
import { UserInfo } from '@ucap-webmessenger/protocol-sync';
import {
  ScheduleSendDialogComponent,
  ScheduleSendDialogData,
  ScheduleSendDialogResult
} from '../dialogs/schedule-send.dialog.component';
import { RoleCode } from '@ucap-webmessenger/protocol-authentication';
import { EmployeeType } from '@ucap-webmessenger/protocol-room';
import { TranslateService } from '@ngx-translate/core';
import { CommonApiService } from '@ucap-webmessenger/api-common';

const ATTR_FILE = 'UCAP_ATTR_FILE';

interface Content {
  contentType: ContentType;
  content: string | File;
  resSeq: number;
}

export interface Message {
  category: CategoryType;
  type: MessageType;
  title: string;
  listOrder: ContentType[];
  textContent: { text: string }[];
  recvUserList: { userSeq: number; userName: string }[];
  files?: File[];
  fileUploadItem?: FileUploadItem;
  reservationTime?: string;
  smsYn?: boolean;
}
export interface MessageModify extends Message {
  resSeqList: number[];
  reservationTime: string;
  msgId: number;
}

@Component({
  selector: 'ucap-message-write',
  templateUrl: './write.component.html',
  styleUrls: ['./write.component.scss'],
  animations: ucapAnimations
})
export class WriteComponent implements OnInit, OnDestroy, AfterViewInit {
  @Input()
  set curReceiverList(userInfoList: UserInfo[]) {
    if (!!userInfoList && userInfoList.length > 0) {
      this.receiverList = userInfoList;
    }
  }
  @Input()
  detail?: DetailResponse;
  @Input()
  detailContents?: string;
  @Input()
  isModify = false;
  @Input()
  fileAllowSize: number;

  @Output()
  send = new EventEmitter<Message | MessageModify>();
  @Output()
  cancel = new EventEmitter<void>();
  @Output()
  selectReceiver = new EventEmitter<UserInfo[]>();

  @ViewChild('editor', { static: true })
  editor: ElementRef<HTMLDivElement>;
  @ViewChild('fileInput', { static: true })
  fileInput: ElementRef<HTMLInputElement>;

  messageWriteForm: FormGroup;
  oldAttachmentList: DetailContent[] = [];
  attachmentList: File[];
  fileUploadItem: FileUploadItem;
  receiverList: UserInfo[] = [];
  contentLength = 0;

  constructor(
    private formBuilder: FormBuilder,
    private dialogService: DialogService,
    private changeDetectorRef: ChangeDetectorRef,
    private translateService: TranslateService,
    private commonApiService: CommonApiService,
    private logger: NGXLogger
  ) {}

  ngOnInit() {
    this.messageWriteForm = this.formBuilder.group({
      title: ['', []]
    });

    if (this.isModify) {
      if (!!this.detail.msgInfo.title) {
        this.messageWriteForm.setValue({ title: this.detail.msgInfo.title });
      }

      if (!!this.detailContents) {
        this.editor.nativeElement.innerHTML = this.detailContents;
        this.onInputEditor();
      }

      this.detail.contents.forEach(content => {
        if (content.resType === ContentType.AttachFile) {
          this.oldAttachmentList.push(content);
        }
      });
    }
  }

  ngOnDestroy(): void {}

  ngAfterViewInit(): void {}

  onClickDeleteOldAttachment(oldAttachment: DetailContent) {
    this.oldAttachmentList = this.oldAttachmentList.filter(
      detailContent => detailContent.resSeq !== oldAttachment.resSeq
    );
  }
  onClickDelelteAttachment(attachment: File) {
    this.attachmentList = this.attachmentList.filter(
      attFile => attFile !== attachment
    );
  }

  onClickImage() {
    this.fileInput.nativeElement.setAttribute('accept', 'image/*');
    this.fileInput.nativeElement.click();
    const self = this;
    this.fileInput.nativeElement.onchange = async () => {
      const fileList: FileList = self.fileInput.nativeElement.files;

      if (!this.validUploadFile(fileList)) {
        return;
      }

      for (let i = 0; i < fileList.length; i++) {
        const file = fileList.item(i);

        const dataUrl = await FileUtil.fromBlobToDataUrl(file);
        const img = document.createElement('img');
        img.src = dataUrl as string;
        img[ATTR_FILE] = file;
        self.insertNode(img);
      }

      self.fileInput.nativeElement.value = '';
      this.fileInput.nativeElement.onchange = undefined;
    };
  }

  onClickAttachment() {
    this.fileInput.nativeElement.removeAttribute('accept');
    this.fileInput.nativeElement.click();
    const self = this;
    this.fileInput.nativeElement.onchange = () => {
      const fileList: FileList = this.fileInput.nativeElement.files;

      if (!this.validUploadFile(fileList)) {
        return;
      }

      if (!self.attachmentList) {
        self.attachmentList = [];
      }

      for (let i = 0; i < fileList.length; i++) {
        const file = fileList.item(i);
        self.attachmentList.push(file);
      }

      self.changeDetectorRef.detectChanges();

      self.fileInput.nativeElement.value = '';
    };
  }

  validUploadFile(fileList: FileList): boolean {
    let valid = true;
    if (this.fileAllowSize > 0) {
      for (let i = 0; i < fileList.length; i++) {
        const file = fileList.item(i);
        if (file.size > this.fileAllowSize * 1024 * 1024) {
          valid = false;
          break;
        }
      }
      if (!valid) {
        this.dialogService.open<
          AlertDialogComponent,
          AlertDialogData,
          AlertDialogResult
        >(AlertDialogComponent, {
          width: '360px',
          data: {
            title: '',
            message: this.translateService.instant(
              'common.file.errors.oversize',
              {
                maxSize: this.fileAllowSize
              }
            )
          }
        });

        return valid;
      }
    }

    const checkExt = this.commonApiService.acceptableExtensionForFileTalk(
      FileUtil.getExtensions(fileList)
    );
    if (!checkExt.accept) {
      this.dialogService.open<
        AlertDialogComponent,
        AlertDialogData,
        AlertDialogResult
      >(AlertDialogComponent, {
        data: {
          title: 'Alert',
          html: `${this.translateService.instant(
            'common.file.errors.notSupporedType'
          )} ${
            checkExt.reject.length > 0
              ? '<br/>(' + checkExt.reject.join(',') + ')'
              : ''
          }`
        }
      });

      return valid;
    }

    return valid;
  }

  onPasteEditor(event: ClipboardEvent) {
    const text = document.createTextNode(
      event.clipboardData.getData('text/plain')
    );
    this.insertNode(text, true);

    this.checkContentLength();

    return false;
  }

  onInputEditor() {
    this.checkContentLength();
  }

  onRemovedReceiver(receiver: UserInfo) {
    const index = this.receiverList.indexOf(receiver);

    if (index >= 0) {
      this.receiverList.splice(index, 1);
    }
  }

  onClickReceiverList() {
    this.selectReceiver.emit(this.receiverList);
  }

  onClickSend() {
    this.sendMessage();
  }

  onClickCancel() {
    this.cancel.emit();
  }

  async onClickSendSchedule() {
    const reservationDate = this.isModify
      ? moment(this.detail.msgInfo.reservationTime)
      : undefined;

    const result = await this.dialogService.open<
      ScheduleSendDialogComponent,
      ScheduleSendDialogData,
      ScheduleSendDialogResult
    >(ScheduleSendDialogComponent, {
      width: '600px',
      height: '600px',
      disableClose: true,
      data: {
        reservationDate
      }
    });

    if (!!result && !!result.scheduleSendDate) {
      this.sendMessage(result.scheduleSendDate);
    }
  }

  private checkContentLength() {
    const result = this.parseContent();
    if (!result) {
      this.contentLength = 0;
      return;
    }

    const { textContent } = result;

    if (!textContent || 0 === textContent.length) {
      this.contentLength = 0;
    } else {
      let len = 0;
      textContent.forEach(c => {
        len += c.text.length;
      });
      this.contentLength = len;
    }
  }

  private sendMessage(reservationDate?: moment.Moment) {
    const result = this.parseContent();
    if (!result) {
      return;
    }

    const { listOrder, textContent, files, resSeqList } = result;

    if (!listOrder || 0 === listOrder.length) {
      return;
    }

    const recvUserList: { userSeq: number; userName: string }[] = [];
    const title = this.messageWriteForm.get('title').value;

    this.receiverList.forEach(v => {
      recvUserList.push({
        userSeq: v.seq,
        userName: v.name
      });
    });

    this.fileUploadItem = FileUploadItem.from();

    if (!this.isModify) {
      const message: Message = {
        category: CategoryType.General,
        type: !!reservationDate ? MessageType.Reservation : MessageType.Send,
        title,
        listOrder,
        textContent,
        recvUserList,
        reservationTime: !!reservationDate
          ? `${reservationDate.format('YYYY-MM-DD HH:mm')}:00`
          : undefined,
        files,
        fileUploadItem: this.fileUploadItem
      };
      this.send.emit(message);
    } else {
      const message: MessageModify = {
        category: CategoryType.General,
        type: !!reservationDate ? MessageType.Reservation : MessageType.Send,
        msgId: this.detail.msgInfo.msgId,
        title,
        listOrder,
        resSeqList,
        textContent,
        recvUserList,
        reservationTime: !!reservationDate
          ? `${reservationDate.format('YYYY-MM-DD HH:mm')}:00`
          : undefined,
        files,
        fileUploadItem: this.fileUploadItem
      };
      this.send.emit(message);
    }
  }

  private parseContent():
    | {
        listOrder: ContentType[];
        textContent: { text: string }[];
        files: File[];
        resSeqList: number[];
      }
    | undefined {
    const contentList: Content[] = this.generateContent();
    if (!contentList || 0 === contentList.length) {
      return;
    }

    const listOrder: ContentType[] = [];
    const textContent: { text: string }[] = [];
    const files: File[] = [];
    const resSeqList: number[] = [];

    contentList.forEach(v => {
      listOrder.push(v.contentType);
      resSeqList.push(v.resSeq);
      switch (v.contentType) {
        case ContentType.Text:
          let content = v.content as string;
          if ('\n' === content.charAt(content.length - 1)) {
            content = content.substring(0, content.length - 1);
          }
          textContent.push({ text: content });
          break;
        case ContentType.Image:
        case ContentType.AttachFile:
          {
            if (v.resSeq === 0) {
              files.push(v.content as File);
            }
          }
          break;
        default:
          break;
      }
    });

    return {
      listOrder,
      textContent,
      files,
      resSeqList
    };
  }

  private generateContent(): Content[] {
    const contentList: Content[] = [];

    this.editor.nativeElement.childNodes.forEach((v, k) => {
      this.parseNode(contentList, v);
    });

    if (!!this.oldAttachmentList && 0 < this.oldAttachmentList.length) {
      this.oldAttachmentList.forEach(v => {
        contentList.push({
          contentType: ContentType.AttachFile,
          content: v.resContent,
          resSeq: v.resSeq
        });
      });
    }

    if (!!this.attachmentList && 0 < this.attachmentList.length) {
      this.attachmentList.forEach(v => {
        contentList.push({
          contentType: ContentType.AttachFile,
          content: v,
          resSeq: 0
        });
      });
    }

    return contentList;
  }

  private parseNode(contentList: Content[], node: ChildNode) {
    switch (node.nodeType) {
      case Node.ELEMENT_NODE:
        {
          if (0 < node.childNodes.length) {
            let prevNode: ChildNode;
            node.childNodes.forEach(v => {
              if (
                !!prevNode &&
                'IMG' === prevNode.nodeName &&
                'BR' === v.nodeName
              ) {
                prevNode = v;
                return;
              }
              prevNode = v;
              this.parseNode(contentList, v);
            });
          } else {
            if ('IMG' === node.nodeName) {
              const img: HTMLImageElement = node as HTMLImageElement;
              this.appendNode(
                contentList,
                ContentType.Image,
                img[ATTR_FILE],
                !!img.getAttribute('id') ? Number(img.getAttribute('id')) : 0
              );
            } else if ('BR' === node.nodeName) {
              this.appendNode(contentList, ContentType.Text, `\n`);
            } else {
            }
          }
        }
        break;
      case Node.TEXT_NODE:
        this.appendNode(contentList, ContentType.Text, node.textContent);
        break;

      default:
        break;
    }
  }

  private appendNode(
    contentList: Content[],
    contentType: ContentType,
    content: string | File,
    resSeq: number = 0
  ) {
    const prevContent = contentList[contentList.length - 1];
    switch (contentType) {
      case ContentType.Text:
        if (!!prevContent && ContentType.Text === prevContent.contentType) {
          prevContent.content = `${prevContent.content}${content}`;
        } else {
          contentList.push({
            contentType: ContentType.Text,
            content,
            resSeq
          });
        }
        break;
      default:
        contentList.push({ contentType, content, resSeq });
        break;
    }
  }

  private insertNode(node: Node, removeSelected: boolean = false) {
    const selection: Selection = document.getSelection();
    const range: Range = selection.getRangeAt(0);

    let inEditor = false;
    if (removeSelected) {
      // onPaste
      selection.empty();
      inEditor = true;
    } else {
      inEditor = this.inEditor(selection.anchorNode);
    }

    if (inEditor) {
      range.insertNode(node);
    } else {
      this.editor.nativeElement.appendChild(node);
    }
    selection.empty();
  }

  private inEditor(el) {
    if (!!el.classList) {
      let root = false;
      (el.classList as DOMTokenList).forEach(className => {
        if (className === 'ucap-message-write') {
          root = true;
        }
      });

      if (!!root) {
        return false;
      }
    }

    if (el.tagName === 'BODY') {
      return false;
    }

    if (
      !!el.className &&
      el.className === 'ucap-message-write-editor' &&
      el.getAttribute('contenteditable')
    ) {
      return true;
    } else {
      return this.inEditor(el.parentNode);
    }
  }
}
