/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *     http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {
  ChangeDetectionStrategy,
  ChangeDetectorRef,
  Component,
  ElementRef,
  EventEmitter,
  Inject,
  Input,
  OnInit,
  Output,
  ViewChild
} from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { NzMessageService } from 'ng-zorro-antd/message';
import { NzModalService } from 'ng-zorro-antd/modal';

import { MessageListener, MessageListenersManager } from '@zeppelin/core';
import { TRASH_FOLDER_ID_TOKEN } from '@zeppelin/interfaces';
import { MessageReceiveDataTypeMap, Note, OP, RevisionListItem } from '@zeppelin/sdk';
import { MessageService, NotebookService, NoteStatusService, SaveAsService, TicketService } from '@zeppelin/services';
import { NoteCreateComponent, ShortcutComponent } from '@zeppelin/share';

@Component({
  selector: 'zeppelin-notebook-action-bar',
  templateUrl: './action-bar.component.html',
  styleUrls: ['./action-bar.component.less'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class NotebookActionBarComponent extends MessageListenersManager implements OnInit {
  @Input() note!: Exclude<Note['note'], undefined>;
  @Input() isOwner = true;
  @Input() looknfeel: 'report' | 'default' | 'simple' = 'default';
  @Input() noteRevisions: RevisionListItem[] = [];
  @Input() currentRevision?: string;
  @Input() collaborativeMode = false;
  @Input() collaborativeModeUsers: string[] = [];
  @Input() revisionView = false;
  @Input() activatedExtension: 'interpreter' | 'permissions' | 'revisions' | 'hide' = 'hide';
  @Output() readonly activatedExtensionChange = new EventEmitter<
    'interpreter' | 'permissions' | 'revisions' | 'hide'
  >();
  @Output() readonly editorHideChange = new EventEmitter<boolean>();
  @Output() readonly tableHideChange = new EventEmitter<boolean>();
  @Output() readonly handleSearch = new EventEmitter<string>();
  @ViewChild('searchInput', { static: false }) searchInputRef?: ElementRef<HTMLInputElement>;
  lfOption: Array<'report' | 'default' | 'simple'> = ['default', 'simple', 'report'];
  isRevisionSupported: boolean = false;
  isNoteParagraphRunning = false;
  principal = this.ticketService.ticket.principal;
  editorHide = false;
  commitVisible = false;
  tableHide = false;
  isSearchMenuVisible = false;
  searchText = '';
  replaceText = '';
  cronOption = [
    { name: 'None', value: undefined },
    { name: '1m', value: '0 0/1 * * * ?' },
    { name: '5m', value: '0 0/5 * * * ?' },
    { name: '1h', value: '0 0 0/1 * * ?' },
    { name: '3h', value: '0 0 0/3 * * ?' },
    { name: '6h', value: '0 0 0/6 * * ?' },
    { name: '12h', value: '0 0 0/12 * * ?' },
    { name: '1d', value: '0 0 0 * * ?' }
  ];

  updateNoteName(name: string) {
    const trimmedNewName = name.trim();
    if (trimmedNewName.length > 0 && this.note.name !== trimmedNewName) {
      this.note.name = trimmedNewName;
      this.messageService.noteRename(this.note.id, this.note.name, true);
    }
  }

  visitRevision(revision: RevisionListItem) {
    if (revision.id) {
      if (revision.id === 'Head') {
        this.router.navigate(['/notebook', this.activatedRoute.snapshot.params.noteId]).then();
      } else {
        this.router.navigate(['/notebook', this.activatedRoute.snapshot.params.noteId, 'revision', revision.id]).then();
      }
    } else {
      this.nzMessageService.warning('There is a problem with this Revision');
    }
  }

  checkpointNote(value: string, e: MouseEvent) {
    e.preventDefault();
    this.commitVisible = false;
    this.messageService.checkpointNote(this.note.id, value);
  }

  setNoteRevision() {
    const { revisionId } = this.activatedRoute.snapshot.params;
    if (revisionId) {
      this.nzModalService.confirm({
        nzTitle: 'Set revision',
        nzContent: 'Set notebook head to current revision?',
        nzOnOk: () => {
          this.messageService.setNoteRevision(this.note.id, revisionId);
        }
      });
    }
  }

  toggleExtension(extension: 'interpreter' | 'permissions' | 'revisions' | 'hide') {
    if (this.activatedExtension === extension) {
      this.activatedExtension = 'hide';
    } else {
      this.activatedExtension = extension;
      if (extension === 'interpreter') {
        this.messageService.getInterpreterBindings(this.note.id);
      }
    }
    this.activatedExtensionChange.emit(this.activatedExtension);
  }

  @MessageListener(OP.PARAGRAPH)
  paragraphUpdate(_data: MessageReceiveDataTypeMap[OP.PARAGRAPH]) {
    this.updateIsNoteParagraphRunning();
    this.cdr.markForCheck();
  }

  runAllParagraphs() {
    this.messageService.runAllParagraphs(
      this.note.id,
      this.note.paragraphs.map(p => ({
        id: p.id,
        title: p.title,
        paragraph: p.text,
        config: p.config,
        params: p.settings.params
      }))
    );
  }

  clearAllParagraphOutput() {
    this.messageService.paragraphClearAllOutput(this.note.id);
  }

  setCronScheduler(cronExpr: string | undefined) {
    if (cronExpr) {
      if (!this.note.config.cronExecutingUser) {
        this.note.config.cronExecutingUser = this.ticketService.ticket.principal;
      }
      if (!this.note.config.cronExecutingRoles) {
        this.note.config.cronExecutingRoles = this.ticketService.ticket.roles;
      }
    } else {
      this.note.config.cronExecutingUser = '';
      this.note.config.cronExecutingRoles = '';
    }
    this.note.config.cron = cronExpr;
    this.setConfig();
  }

  setReleaseResource(releaseresource: boolean) {
    this.note.config.releaseresource = releaseresource;
    this.setConfig();
  }

  setConfig(config?: Exclude<Note['note'], undefined>['config']) {
    if (config) {
      this.note.config = config;
    }

    this.messageService.updateNote(this.note.id, this.note.name, this.note.config);
  }

  cloneNote() {
    this.nzModalService.create({
      nzTitle: 'Clone Note',
      nzContent: NoteCreateComponent,
      nzComponentParams: {
        cloneNote: this.note
      },
      nzFooter: null
    });
  }

  exportNote() {
    if (!this.ticketService.configuration) {
      throw new Error('Configuration is not loaded');
    }
    const sizeLimit = +this.ticketService.configuration['zeppelin.websocket.max.text.message.size'];
    const jsonContent = JSON.stringify(this.note);
    if (jsonContent.length > sizeLimit) {
      this.nzModalService.confirm({
        nzTitle: `Note size exceeds importable limit (${sizeLimit})`,
        nzContent: 'Do you still want to export this note?',
        nzOnOk: () => {
          this.saveAsService.saveAs(jsonContent, this.note.name, 'zpln');
        }
      });
    } else {
      this.saveAsService.saveAs(jsonContent, this.note.name, 'zpln');
    }
  }

  reloadNote() {
    this.messageService.reloadNote(this.note.id);
  }

  toggleAllEditor() {
    this.editorHide = !this.editorHide;
    this.editorHideChange.emit(this.editorHide);
  }

  toggleAllTable() {
    this.tableHide = !this.tableHide;
    this.tableHideChange.emit(this.tableHide);
  }

  searchCode() {
    this.handleSearch.emit(this.searchText);
  }

  onSearchMenuOpenChange(open: boolean) {
    if (open) {
      setTimeout(() => {
        this.searchInputRef?.nativeElement?.focus();
      }, 0);
    } else {
      this.searchText = '';
      this.searchCode();
    }
  }

  // TODO: Implement logic to find the previous search match in the notebook editor
  onFindPrevClick(_searchText: string) {}

  // TODO: Implement logic to find the next search match in the notebook editor
  onFindNextClick(_searchText: string) {}

  // TODO: Implement logic to replace the current search match with the replacement text
  onReplaceClick(_searchText: string, _replaceText: string) {}

  // TODO: Implement logic to replace all search matches with the replacement text
  onReplaceAllClick(searchText: string, _replaceText: string) {
    this.handleSearch.emit(searchText);
  }

  openSearchMenu() {
    this.isSearchMenuVisible = true;
    this.cdr.markForCheck();
  }

  deleteNote() {
    this.messageService.deleteNote(this.note.id);
  }

  moveNoteToTrash() {
    this.messageService.moveNoteToTrash(this.note.id);
    this.router.navigate(['/']);
  }

  get isTrash() {
    return this.noteStatusService.isTrash(this.note);
  }

  get viewOnly(): boolean {
    return this.noteStatusService.viewOnly(this.note);
  }

  updateIsNoteParagraphRunning() {
    this.isNoteParagraphRunning = this.noteStatusService.isNoteParagraphRunning(this.note);
  }

  showShortCut() {
    this.nzModalService.info({
      nzTitle: `Shortcut Info`,
      nzWidth: '600px',
      nzContent: ShortcutComponent
    });
  }

  togglePermissions() {
    this.toggleExtension('permissions');
  }

  setLookAndFeel(lf: 'report' | 'default' | 'simple') {
    this.note.config.looknfeel = lf;
    if (!this.activatedRoute.snapshot.params.revisionId) {
      this.messageService.updateNote(this.note.id, this.note.name, this.note.config);
    }
  }

  toggleNotePersonalizedMode() {
    const modeText = this.note.config.personalizedMode === 'true' ? 'collaborate' : 'personalize';
    if (this.isOwner) {
      this.nzModalService.confirm({
        nzTitle: 'Setting the result display',
        nzContent: `Do you want to ${modeText} your analysis?`,
        nzOnOk: () => {
          if (this.note.config.personalizedMode === undefined || this.note.config.personalizedMode === 'true') {
            this.note.config.personalizedMode = 'false';
          } else {
            this.note.config.personalizedMode = 'true';
          }
          this.messageService.updatePersonalizedMode(this.note.id, this.note.config.personalizedMode);
        }
      });
    }
  }

  get getCronOptionNameFromValue() {
    if (!this.note.config.cron) {
      return '';
    } else if (this.cronOption.find(cron => cron.value === this.note.config.cron)) {
      return this.cronOption.find(cron => cron.value === this.note.config.cron)!.name;
    } else {
      return this.note.config.cron;
    }
  }

  constructor(
    public messageService: MessageService,
    @Inject(TRASH_FOLDER_ID_TOKEN) public TRASH_FOLDER_ID: string,
    private nzModalService: NzModalService,
    private ticketService: TicketService,
    private nzMessageService: NzMessageService,
    private router: Router,
    private cdr: ChangeDetectorRef,
    private noteStatusService: NoteStatusService,
    private notebookService: NotebookService,
    private activatedRoute: ActivatedRoute,
    private saveAsService: SaveAsService
  ) {
    super(messageService);
    this.updateIsNoteParagraphRunning();
    if (!this.ticketService.configuration) {
      throw new Error('Configuration is not loaded');
    }
  }

  ngOnInit(): void {
    this.notebookService.capabilities().subscribe(c => {
      this.isRevisionSupported = c.isRevisionSupported;
      this.cdr.markForCheck();
    });
  }
}
