import { Component } from '@angular/core';
import {
  CdkDragDrop,
  moveItemInArray,
  transferArrayItem,
} from '@angular/cdk/drag-drop';
import { Spell } from 'src/interfaces/interfaces';
import { DataService } from 'src/services/data/data.service';
import { ModalService } from 'src/services/modal/modal.service';
import { SpellsService } from 'src/services/spells/spells.service';
import { FullSpellcardComponent } from 'src/app/shared-components/full-spellcard/full-spellcard.component';
import { Router } from '@angular/router';
import { FavoriteSpellsModalComponent } from '../journal-stats/weapons-container/spell-table/favorite-spells-modal/favorite-spells-modal.component';
@Component({
  selector: 'app-journal-spellcards',
  templateUrl: './journal-spellcards.component.html',
  styleUrls: ['./journal-spellcards.component.scss'],
})
export class JournalSpellcardsComponent {
  public level0: Spell[] = [];
  public level1: Spell[] = [];
  public level2: Spell[] = [];
  public level3: Spell[] = [];
  public level4: Spell[] = [];
  public level5: Spell[] = [];
  public level6: Spell[] = [];
  public level7: Spell[] = [];
  public level8: Spell[] = [];
  public level9: Spell[] = [];

  public showSpellList: boolean[] = [
    true,
    true,
    true,
    true,
    true,
    true,
    true,
    true,
    true,
    true,
  ];

  public draggingIndex: number | undefined;

  public constructor(
    private dataAccessor: DataService,
    private modalAccessor: ModalService,
    public spellsService: SpellsService,
    private router: Router,
  ) {
    this.loadSpells();
    this.hideEmptySpelllists();
  }

  ///////// FUNCTIONS //////////

  /**
   * Collects all ids of the spells in the spell list of a given level.
   * They are passed to the add-card to exclude them from the list of available spells.
   * @param level The level of the spell.
   */
  public getUsedIDs(level: number): number[] {
    const usedIDs: number[] = [];
    this.getSpellList(level).forEach((spell) => {
      usedIDs.push(spell.id);
    });
    return usedIDs;
  }

  /**
   * Opens a modal to show the full spellcard of a spell. The modal also allows the user to add the spell to the favorites and remove it again.
   * Moreover, the user can remove the spell from the prepared spells list.
   * @param spell The spell to show.
   * @param level The level of the spell.
   * @param spellIndex The index of the spell in the spell list.
   */
  public showFullSpellcard(
    spell: Spell,
    level: number,
    spellIndex: number,
  ): void {
    const favorites = this.dataAccessor.favoriteSpells;
    const alreadyInFavorites = favorites.some(
      (currentSpell) => currentSpell.id === spell.id,
    );
    this.modalAccessor.openModal(FullSpellcardComponent, {
      spell: spell,
      origin: 'spellcards',
      alreadyInFavorites: alreadyInFavorites,
    });
    const actionSubscription = this.modalAccessor.action$.subscribe(
      (message) => {
        if (message.action === 'addToFavorites') {
          this.dataAccessor.addFavoriteSpell(spell);
        } else if (message.action === 'removeFromFavorites') {
          this.dataAccessor.removeFavoriteSpell(spell);
        }
      },
    );
    const resultSubscription = this.modalAccessor.result$.subscribe(
      (result) => {
        resultSubscription.unsubscribe();
        actionSubscription.unsubscribe();
        if (result.state === 'remove') {
          this.dataAccessor.removeFavoriteSpell(spell);
          this.getSpellList(level).splice(spellIndex, 1);
          this.updateSpellsInDatabase(level);
        }
      },
    );
  }

  /**
   * Adds a given spell to the spell list specified by level.
   * Also updates the spell list in the database.
   * @param spell The spell to add.
   * @param level The level to add the spell to.
   */
  public addSpell(spell: Spell, level: number) {
    this.getSpellList(level).push(spell);
    this.updateSpellsInDatabase(level);
  }

  /**
   * Returns the reference to the spell list specified by the level.
   * @param level Specifies the level of the spell list.
   * @returns Returns the spell list specified by the level.
   */
  public getSpellList(level: number): Spell[] {
    switch (level) {
      case 0:
        return this.level0;
      case 1:
        return this.level1;
      case 2:
        return this.level2;
      case 3:
        return this.level3;
      case 4:
        return this.level4;
      case 5:
        return this.level5;
      case 6:
        return this.level6;
      case 7:
        return this.level7;
      case 8:
        return this.level8;
      case 9:
        return this.level9;
      default:
        return [];
    }
  }

  public toggleSpellList(index: number) {
    this.showSpellList[index] = !this.showSpellList[index];
  }

  public drop(event: CdkDragDrop<any[]>) {
    if (event.previousContainer === event.container) {
      moveItemInArray(
        event.container.data,
        event.previousIndex,
        event.currentIndex,
      );
      this.updateSpellsInDatabase(this.getIndex(event.previousContainer.id));
      // update database with one level
    } else {
      transferArrayItem(
        event.previousContainer.data,
        event.container.data,
        event.previousIndex,
        event.currentIndex,
      );
      this.updateSpellsInDatabase(this.getIndex(event.previousContainer.id));

      // Update level of moved spell
      const movedSpell = event.container.data[event.currentIndex];
      const newContainer = event.container.id;

      switch (newContainer) {
        case 'cdk-drop-list-0':
          movedSpell.level = 0;
          this.updateSpellsInDatabase(0);
          if (movedSpell.isCustom) {
            this.dataAccessor.updateCustomSpell(movedSpell);
          }
          break;
        case 'cdk-drop-list-1':
          movedSpell.level = 1;
          this.updateSpellsInDatabase(1);
          if (movedSpell.isCustom) {
            this.dataAccessor.updateCustomSpell(movedSpell);
          }
          break;
        case 'cdk-drop-list-2':
          movedSpell.level = 2;
          this.updateSpellsInDatabase(2);
          if (movedSpell.isCustom) {
            this.dataAccessor.updateCustomSpell(movedSpell);
          }
          break;
        case 'cdk-drop-list-3':
          movedSpell.level = 3;
          this.updateSpellsInDatabase(3);
          if (movedSpell.isCustom) {
            this.dataAccessor.updateCustomSpell(movedSpell);
          }
          break;
        case 'cdk-drop-list-4':
          movedSpell.level = 4;
          this.updateSpellsInDatabase(4);
          if (movedSpell.isCustom) {
            this.dataAccessor.updateCustomSpell(movedSpell);
          }
          break;
        case 'cdk-drop-list-5':
          movedSpell.level = 5;
          this.updateSpellsInDatabase(5);
          if (movedSpell.isCustom) {
            this.dataAccessor.updateCustomSpell(movedSpell);
          }
          break;
        case 'cdk-drop-list-6':
          movedSpell.level = 6;
          this.updateSpellsInDatabase(6);
          if (movedSpell.isCustom) {
            this.dataAccessor.updateCustomSpell(movedSpell);
          }
          break;
        case 'cdk-drop-list-7':
          movedSpell.level = 7;
          this.updateSpellsInDatabase(7);
          if (movedSpell.isCustom) {
            this.dataAccessor.updateCustomSpell(movedSpell);
          }
          break;
        case 'cdk-drop-list-8':
          movedSpell.level = 8;
          this.updateSpellsInDatabase(8);
          if (movedSpell.isCustom) {
            this.dataAccessor.updateCustomSpell(movedSpell);
          }
          break;
        case 'cdk-drop-list-9':
          movedSpell.level = 9;
          this.updateSpellsInDatabase(9);
          if (movedSpell.isCustom) {
            this.dataAccessor.updateCustomSpell(movedSpell);
          }
          break;
      }
    }
  }

  private getIndex(id: string): number {
    switch (id) {
      case 'cdk-drop-list-0':
        return 0;
      case 'cdk-drop-list-1':
        return 1;
      case 'cdk-drop-list-2':
        return 2;
      case 'cdk-drop-list-3':
        return 3;
      case 'cdk-drop-list-4':
        return 4;
      case 'cdk-drop-list-5':
        return 5;
      case 'cdk-drop-list-6':
        return 6;
      case 'cdk-drop-list-7':
        return 7;
      case 'cdk-drop-list-8':
        return 8;
      case 'cdk-drop-list-9':
        return 9;
      default:
        return -1;
    }
  }

  /**
   * When a spell is dragged, the index, representing the level of the spell is stored in the draggingIndex variable.
   * @param index The spell's level.
   */
  public dragStart(index: number) {
    this.draggingIndex = index;
  }

  /**
   * Checks if the dragged element was dropped over the removal card,
   * the spell is removed from the prepared list and the favorites list.
   * @param event The event that contains the dragged element.
   */
  public dragEnd(event: any) {
    if (event.event.target.classList.contains('removal-card')) {
      this.dataAccessor.removeFavoriteSpell(
        this.getSpellList(this.draggingIndex!)[
          event.source.element.nativeElement.id
        ],
      );
      this.getSpellList(this.draggingIndex!).splice(
        event.source.element.nativeElement.id,
        1,
      );
      this.updateSpellsInDatabase(this.draggingIndex!);
    }
    this.draggingIndex = undefined;
  }

  private loadSpells(): void {
    this.level0 = this.dataAccessor.spellLevel0;
    this.level1 = this.dataAccessor.spellLevel1;
    this.level2 = this.dataAccessor.spellLevel2;
    this.level3 = this.dataAccessor.spellLevel3;
    this.level4 = this.dataAccessor.spellLevel4;
    this.level5 = this.dataAccessor.spellLevel5;
    this.level6 = this.dataAccessor.spellLevel6;
    this.level7 = this.dataAccessor.spellLevel7;
    this.level8 = this.dataAccessor.spellLevel8;
    this.level9 = this.dataAccessor.spellLevel9;
  }

  private hideEmptySpelllists(): void {
    this.showSpellList = [
      this.level0.length > 0,
      this.level1.length > 0,
      this.level2.length > 0,
      this.level3.length > 0,
      this.level4.length > 0,
      this.level5.length > 0,
      this.level6.length > 0,
      this.level7.length > 0,
      this.level8.length > 0,
      this.level9.length > 0,
    ];
  }

  private updateSpellsInDatabase(level: number): void {
    switch (level) {
      case 0:
        this.dataAccessor.spellLevel0 = this.level0;
        break;
      case 1:
        this.dataAccessor.spellLevel1 = this.level1;
        break;
      case 2:
        this.dataAccessor.spellLevel2 = this.level2;
        break;
      case 3:
        this.dataAccessor.spellLevel3 = this.level3;
        break;
      case 4:
        this.dataAccessor.spellLevel4 = this.level4;
        break;
      case 5:
        this.dataAccessor.spellLevel5 = this.level5;
        break;
      case 6:
        this.dataAccessor.spellLevel6 = this.level6;
        break;
      case 7:
        this.dataAccessor.spellLevel7 = this.level7;
        break;
      case 8:
        this.dataAccessor.spellLevel8 = this.level8;
        break;
      case 9:
        this.dataAccessor.spellLevel9 = this.level9;
        break;
    }
  }

  public navigateToSpellbook(): void {
    this.router.navigate(['journal/spellbook']);
  }

  public openFavoritesModal(): void {
    this.modalAccessor.openModal(FavoriteSpellsModalComponent, {
      preparedSpells: [
        ...this.level0,
        ...this.level1,
        ...this.level2,
        ...this.level3,
        ...this.level4,
        ...this.level5,
        ...this.level6,
        ...this.level7,
        ...this.level8,
        ...this.level9,
      ],
      selectedSpells: this.dataAccessor.favoriteSpells,
    });
    const resultSubscription = this.modalAccessor.result$.subscribe(
      (result) => {
        if (result.state === 'update') {
          this.dataAccessor.favoriteSpells = result.data;
        }
        resultSubscription.unsubscribe();
      },
    );
  }
}
