import { Component, Inject, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { NotificationService } from '../../../../services/util/notification.service';
import { TopicCloudConfirmDialogComponent } from '../topic-cloud-confirm-dialog/topic-cloud-confirm-dialog.component';
import { UserRole } from '../../../../models/user-roles.enum';
import { TranslateService } from '@ngx-translate/core';
import { LanguageService } from '../../../../services/util/language.service';
import { TopicCloudAdminService } from '../../../../services/util/topic-cloud-admin.service';
import { TopicCloudAdminData, Labels, spacyLabels } from './TopicCloudAdminData';
import { KeywordOrFulltext } from './TopicCloudAdminData';
import { User } from '../../../../models/user';
import { Comment } from '../../../../models/comment';
import { CommentService } from '../../../../services/http/comment.service';
import { WsCommentServiceService } from '../../../../services/websockets/ws-comment-service.service';
import { TSMap } from 'typescript-map';

@Component({
  selector: 'app-topic-cloud-administration',
  templateUrl: './topic-cloud-administration.component.html',
  styleUrls: ['./topic-cloud-administration.component.scss']
})
export class TopicCloudAdministrationComponent implements OnInit, OnDestroy {
  public panelOpenState = false;
  public considerVotes: boolean;
  public profanityFilter: boolean;
  public blacklistIsActive: boolean;
  blacklist: string[] = [];
  blacklistSubscription = undefined;
  keywordOrFulltextENUM = KeywordOrFulltext;
  newKeyword = undefined;
  edit = false;
  isCreatorOrMod: boolean;
  enterProfanityWord = false;
  enterBlacklistWord = false;
  newProfanityWord: string = undefined;
  newBlacklistWord: string = undefined;
  sortMode = 'alphabetic';
  searchedKeyword = undefined;
  searchMode = false;
  filteredKeywords: Keyword[] = [];
  showProfanityList = false;
  showBlacklistWordList = false;
  showSettingsPanel = false;
  keywordORfulltext: string = undefined;
  userRole: UserRole;
  allSelectedDE = true;
  allSelectedEN = true;
  spacyLabels: Labels;
  wantedLabels: {
    de: string[];
    en: string[];
  };

  keywords: Keyword[] = [];
  private topicCloudAdminData: TopicCloudAdminData;

  constructor(
    @Inject(MAT_DIALOG_DATA) public data: Data,
    public cloudDialogRef: MatDialogRef<TopicCloudAdministrationComponent>,
    public confirmDialog: MatDialog,
    private notificationService: NotificationService,
    private translateService: TranslateService,
    private langService: LanguageService,
    private topicCloudAdminService: TopicCloudAdminService,
    private commentService: CommentService,
    private wsCommentServiceService: WsCommentServiceService) {
      this.langService.langEmitter.subscribe(lang => {
        this.translateService.use(lang);
      });
    }

  ngOnInit(): void {
    this.wsCommentServiceService.getCommentStream(localStorage.getItem('roomId')).subscribe(_ => this.initKeywords());
    this.blacklistSubscription = this.topicCloudAdminService.getBlacklist().subscribe(list => this.blacklist = list);
    this.isCreatorOrMod = this.data ? (this.data.user.role !== UserRole.PARTICIPANT) : true;
    this.translateService.use(localStorage.getItem('currentLang'));
    this.spacyLabels = spacyLabels;
    this.wantedLabels = undefined;
    this.setDefaultAdminData();
    this.initKeywords();
  }

  ngOnDestroy(){
    this.setAdminData();
    if(this.blacklistSubscription !== undefined){
      this.blacklistSubscription.unsubscribe();
    }
  }

  initKeywords(){
    this.commentService.getFilteredComments(localStorage.getItem('roomId')).subscribe(comments => {
      this.keywords = [];
      comments.map(comment => {
        const keywords = this.keywordORfulltext === KeywordOrFulltext[0] ? comment.keywordsFromQuestioner : comment.keywordsFromSpacy;
        keywords.map(_keyword => {
          const existingKey = this.checkIfKeywordExists(_keyword);
          if (existingKey){
            existingKey.vote += comment.score;
            if (this.checkIfCommentExists(existingKey.comments, comment.id)){
              existingKey.comments.push(comment);
            }
          } else {
            const keyword: Keyword = {
              keyword: _keyword,
              comments: [comment],
              vote: comment.score
            };
            this.keywords.push(keyword);
          }
        });
      });
      this.sortQuestions();
    });
  }

  checkIfCommentExists(comments: Comment[], id: string): boolean{
    return comments.filter(comment => comment.id === id).length === 0;
  }

  setAdminData() {
    this.topicCloudAdminData = {
      blacklist: [],
      wantedLabels: {
        de: this.wantedLabels.de,
        en: this.wantedLabels.en
      },
      considerVotes: this.considerVotes,
      profanityFilter: this.profanityFilter,
      blacklistIsActive: this.blacklistIsActive,
      keywordORfulltext: KeywordOrFulltext[this.keywordORfulltext]
    };
    this.topicCloudAdminService.setAdminData(this.topicCloudAdminData);
  }

  setDefaultAdminData() {
    this.topicCloudAdminData = this.topicCloudAdminService.getDefaultAdminData;
    if (this.topicCloudAdminData) {
      this.considerVotes = this.topicCloudAdminData.considerVotes;
      this.profanityFilter = this.topicCloudAdminData.profanityFilter;
      this.blacklistIsActive = this.topicCloudAdminData.blacklistIsActive;
      this.keywordORfulltext = KeywordOrFulltext[this.topicCloudAdminData.keywordORfulltext];
      this.wantedLabels = {
        de: this.topicCloudAdminData.wantedLabels.de,
        en: this.topicCloudAdminData.wantedLabels.en
      };
    }
  }

  getKeywordWithoutProfanity(keyword: string): string {
    return this.topicCloudAdminService.filterProfanityWords(keyword);
  }

  getProfanityList() {
    return this.topicCloudAdminService.getCustomProfanityList();
  }

  sortQuestions(sortMode?: string) {
    if (sortMode !== undefined) {
      this.sortMode = sortMode;
    }

    switch (this.sortMode) {
      case 'alphabetic':
        this.keywords.sort((a, b) => a.keyword.localeCompare(b.keyword));
        break;
      case 'questionsCount':
        this.keywords.sort((a, b) => b.comments.length - a.comments.length);
        break;
      case 'voteCount':
        this.keywords.sort((a, b) => b.vote - a.vote);
        break;
    }
  }

  checkIfThereAreQuestions() {
    if (this.keywords.length === 0){
      this.translateService.get('topic-cloud-dialog.no-keywords-note').subscribe(msg => {
        this.notificationService.show(msg);
      });
      setTimeout(() => {
        this.cloudDialogRef.close();
      }, 0);
    }
  }

  editKeyword(index: number): void {
    this.edit = true;
    setTimeout(() => {
      document.getElementById('edit-input'+ index).focus();
    }, 0);
  }

  deleteKeyword(key: Keyword, message?: string): void{
    key.comments.map(comment => {
      const changes = new TSMap<string, any>();
      let keywords = comment.keywordsFromQuestioner;
      keywords.splice(keywords.indexOf(key.keyword, 0), 1);
      changes.set('keywordsFromQuestioner', JSON.stringify(keywords));
      keywords = comment.keywordsFromSpacy;
      keywords.splice(keywords.indexOf(key.keyword, 0), 1);
      changes.set('keywordsFromSpacy', JSON.stringify(keywords));
      this.updateComment(comment, changes, message);
    });

    if (this.searchMode === true){
      this.searchKeyword();
    }
  }

  updateComment(updatedComment: Comment, changes: TSMap<string, any>, messageTranslate?: string){
    this.commentService.patchComment(updatedComment, changes).subscribe(_ => {
      if (messageTranslate){
        this.translateService.get('topic-cloud-dialog.' + messageTranslate).subscribe(msg => {
          this.notificationService.show(msg);
        });
      }
    },
      error => {
        this.translateService.get('topic-cloud-dialog.changes-gone-wrong').subscribe(msg => {
          this.notificationService.show(msg);
        });
    });
  }

  cancelEdit(): void {
    this.edit = false;
    this.newKeyword = undefined;
  }

  confirmEdit(key: Keyword): void {
    const key2 = this.checkIfKeywordExists(this.newKeyword);
    if (key2){
      this.openConfirmDialog('merge-message', 'merge', key, key2);
    } else {
      key.comments.map(comment => {
        const changes = new TSMap<string, any>();
        let keywords = comment.keywordsFromQuestioner;
        for (let i = 0; i < keywords.length; i++){
          if (keywords[i].toLowerCase() === key.keyword.toLowerCase()){
            keywords[i] = this.newKeyword.trim();
          }
        }
        changes.set('keywordsFromQuestioner', JSON.stringify(keywords));
        keywords = comment.keywordsFromSpacy;
        for (let i = 0; i < keywords.length; i++){
          if (keywords[i].toLowerCase() === key.keyword.toLowerCase()){
            keywords[i] = this.newKeyword.trim();
          }
        }
        changes.set('keywordsFromSpacy', JSON.stringify(keywords));
        this.updateComment(comment, changes, 'keyword-edit');
      });
    }

    this.edit = false;
    this.newKeyword = undefined;
    this.sortQuestions();
    if (this.searchMode){
      this.searchKeyword();
    }
  }

  openConfirmDialog(msg: string, _confirmLabel: string, keyword: Keyword, mergeTarget?: Keyword) {
    const translationPart = 'topic-cloud-confirm-dialog.'+msg;
    const confirmDialogRef = this.confirmDialog.open(TopicCloudConfirmDialogComponent, {
      data: {topic: keyword.keyword, message: translationPart, confirmLabel: _confirmLabel}
    });

    confirmDialogRef.afterClosed().subscribe(result => {
      if (result === 'delete') {
        this.deleteKeyword(keyword, 'keyword-delete');
      } else if (result === 'merge') {
        this.mergeKeywords(keyword, mergeTarget);
      }
    });
  }

  searchKeyword(): void {
    if (!this.searchedKeyword){
      this.searchMode = false;
    } else {
      this.filteredKeywords = this.keywords.filter(keyword =>
        keyword.keyword.toLowerCase().includes(this.searchedKeyword.toLowerCase())
      );
      this.searchMode = true;
    }
  }

  mergeKeywords(key1: Keyword, key2: Keyword) {
    if (key1 !== undefined && key2 !== undefined){
      key1.comments.map(comment => {
        if (this.checkIfCommentExists(key2.comments, comment.id)){
          const changes = new TSMap<string, any>();
          let keywords = comment.keywordsFromQuestioner;
          keywords.push(key2.keyword);
          changes.set('keywordsFromQuestioner', JSON.stringify(keywords));
          keywords = comment.keywordsFromSpacy;
          keywords.push(key2.keyword);
          changes.set('keywordsFromSpacy', JSON.stringify(keywords));
          this.updateComment(comment, changes);
        }
      });
      this.deleteKeyword(key1);
    }
  }

  checkIfKeywordExists(key: string): Keyword {
    for(const keyword of this.keywords){
      if(keyword.keyword.toLowerCase() === key.trim().toLowerCase()){
        return keyword;
      }
    }
    return undefined;
  }

  focusInput(id: string) {
    setTimeout(() => {
      document.getElementById(id).focus();
    }, 100);
  }

  addProfanityWord() {
    this.topicCloudAdminService.addToProfanityList(this.newProfanityWord);
    this.newProfanityWord = undefined;
    if (this.searchMode){
      this.searchKeyword();
    }
  }

  addBlacklistWord() {
    this.topicCloudAdminService.addWordToBlacklist(this.newBlacklistWord);
    this.newBlacklistWord = undefined;
    if (this.searchMode){
      this.searchKeyword();
    }
  }

  removeWordFromProfanityList(word: string) {
    this.topicCloudAdminService.removeFromProfanityList(word);
  }

  removeWordFromBlacklist(word: string) {
    this.topicCloudAdminService.removeWordFromBlacklist(word);
  }

  refreshAllLists() {
    this.searchKeyword();
  }

  selectAllDE() {
    if (this.allSelectedDE) {
      this.wantedLabels.de = []
    } else {
      this.wantedLabels.de = [];
      this.spacyLabels.de.forEach(label => {
        this.wantedLabels.de.push(label.tag);
      });
    }
  }

  selectAllEN() {
    if (this.allSelectedEN) {
      this.wantedLabels.en = [];
      this.spacyLabels.en.forEach(label => {
        this.wantedLabels.en.push(label.tag);
      });
    } else {
      this.wantedLabels.en = []
    }
  }
}

interface Keyword {
  keyword: string;
  comments: Comment[];
  vote: number;
}

export interface Data{
  user: User;
}