import { Component, inject } from '@angular/core';
import { ListService } from 'src/services/list/list.service';
import { ModalService } from 'src/services/modal/modal.service';
import { DataService } from 'src/services/data/data.service';
import { Spell } from 'src/interfaces/interfaces';
import { CustomSpellsModalComponent } from './custom-spells-modal/custom-spells-modal.component';
import { SpellsService } from 'src/services/spells/spells.service';
import { FullSpellcardComponent } from 'src/app/shared-components/full-spellcard/full-spellcard.component';
import { SpellModalComponent } from '../spell-modal/spell-modal.component';
@Component({
  selector: 'app-journal-spellbook',
  templateUrl: './journal-spellbook.component.html',
  styleUrls: ['./journal-spellbook.component.scss'],
})
export class JournalSpellbookComponent {
  public filterAll = false;
  public filterArtificer = false;
  public filterBard = false;
  public filterCleric = false;
  private filterDruid = false;
  private filterPaladin = false;
  private filterRanger = false;
  private filterSorcerer = false;
  private filterWarlock = false;
  private filterWizard = false;
  private previousClasses: string[] = [];

  public currentLevel: number = 0;
  public filteredSpells: Spell[] = [];

  // Services
  public list = inject(ListService);
  public modalAccessor = inject(ModalService);
  public dataAccessor = inject(DataService);
  private spellsService = inject(SpellsService);

  public ngAfterViewInit(): void {
    const className: string = this.dataAccessor.characterData.class;
    this.toggleClassFilter(className);
    this.refreshFilteredSpells();
  }

  // SPELLS FILTER

  /**
   * Toggles the selection of a given class icon. It also updates the filtered spells.
   * When the all classes button was (de-)selects all classes.
   * @param className The provided class which icon will be toggled.
   */
  public toggleClassSelection(className: string) {
    if (className === 'all') {
      if (this.filterAll) {
        this.setAllClassesInactive();
        this.restorePreviousClasses();
        this.refreshFilteredSpells();
      } else {
        this.previousClasses = this.getActiveClasses();
        this.setAllClassesActive();
        this.refreshFilteredSpells();
      }
      return;
    }
    this.toggleClassFilter(className);
    // Hier kann man noch darauf warten, ob vielleicht mehrere Klassen kurz nacheinander ausgewählt werden
    this.refreshFilteredSpells();
  }

  /**
   * Retrieves the filtered spells from the SpellsService.
   */
  public refreshFilteredSpells(isDelayed: boolean = false) {
    if (isDelayed) {
      setTimeout(() => {
        this.filteredSpells = this.spellsService.getSpellsByClasslistAndLevel(
          this.getActiveClasses(),
          this.currentLevel,
        );
      }, 150);
    } else {
      this.filteredSpells = this.spellsService.getSpellsByClasslistAndLevel(
        this.getActiveClasses(),
        this.currentLevel,
      );
    }
  }

  /**
   * Toggles if a given class is used to filter the spells.
   * @param className The class which icon will be toggled.
   */
  private toggleClassFilter(className: string) {
    console.log('Toggle class filter: ' + className);

    this.filterAll = false;
    document.getElementById('all')!.classList.remove('icon-active');
    const isActive: boolean = this.getClassVariabel(className);
    this.setClassFilter(className, !isActive);
    if (isActive) {
      document.getElementById(className)!.classList.remove('icon-active');
    } else {
      document.getElementById(className)!.classList.add('icon-active');
    }
  }

  /**
   * Sets a value to the corresponding variable to a classname.
   * Active classes will be used to filter the spells.
   * @param className The class which variable is looked for.
   * @param value The value to be set.
   */
  private setClassFilter(className: string, value: boolean) {
    switch (className) {
      case 'all':
        this.filterAll = value;
        break;
      case 'artificer':
        this.filterArtificer = value;
        break;
      case 'bard':
        this.filterBard = value;
        break;
      case 'cleric':
        this.filterCleric = value;
        break;
      case 'druid':
        this.filterDruid = value;
        break;
      case 'paladin':
        this.filterPaladin = value;
        break;
      case 'ranger':
        this.filterRanger = value;
        break;
      case 'sorcerer':
        this.filterSorcerer = value;
        break;
      case 'warlock':
        this.filterWarlock = value;
        break;
      case 'wizard':
        this.filterWizard = value;
        break;
    }
  }

  /**
   * Allows to access the corresponding varibale to a classname
   * @param className The class which varibale is looked for.
   * @returns Returns the given class filter variable.
   */
  private getClassVariabel(className: string): boolean {
    switch (className) {
      case 'all':
        return this.filterAll;
      case 'artificer':
        return this.filterArtificer;
      case 'bard':
        return this.filterBard;
      case 'cleric':
        return this.filterCleric;
      case 'druid':
        return this.filterDruid;
      case 'paladin':
        return this.filterPaladin;
      case 'ranger':
        return this.filterRanger;
      case 'sorcerer':
        return this.filterSorcerer;
      case 'warlock':
        return this.filterWarlock;
      case 'wizard':
        return this.filterWizard;
      default:
        return false;
    }
  }

  /**
   * Returns all active classes.
   * @returns Returns all active classes.
   */
  private getActiveClasses(): string[] {
    const activeClasses: string[] = [];
    if (this.filterArtificer) {
      activeClasses.push('artificer');
    }
    if (this.filterBard) {
      activeClasses.push('bard');
    }
    if (this.filterCleric) {
      activeClasses.push('cleric');
    }
    if (this.filterDruid) {
      activeClasses.push('druid');
    }
    if (this.filterPaladin) {
      activeClasses.push('paladin');
    }
    if (this.filterRanger) {
      activeClasses.push('ranger');
    }
    if (this.filterSorcerer) {
      activeClasses.push('sorcerer');
    }
    if (this.filterWarlock) {
      activeClasses.push('warlock');
    }
    if (this.filterWizard) {
      activeClasses.push('wizard');
    }
    return activeClasses;
  }

  /**
   * Sets all class filters to true and activates all class icons.
   * This is used when the all classes button is selected
   */
  private setAllClassesActive(): void {
    this.filterAll = true;
    this.filterArtificer = true;
    this.filterBard = true;
    this.filterCleric = true;
    this.filterDruid = true;
    this.filterPaladin = true;
    this.filterRanger = true;
    this.filterSorcerer = true;
    this.filterWarlock = true;
    this.filterWizard = true;
    document.getElementById('all')!.classList.add('icon-active');
    document.getElementById('artificer')!.classList.add('icon-active');
    document.getElementById('bard')!.classList.add('icon-active');
    document.getElementById('cleric')!.classList.add('icon-active');
    document.getElementById('druid')!.classList.add('icon-active');
    document.getElementById('paladin')!.classList.add('icon-active');
    document.getElementById('ranger')!.classList.add('icon-active');
    document.getElementById('sorcerer')!.classList.add('icon-active');
    document.getElementById('warlock')!.classList.add('icon-active');
    document.getElementById('wizard')!.classList.add('icon-active');
  }

  /**
   * Sets all class filters to false and unselects all class buttons.
   * This is used when the all classes button is deselected.
   */
  private setAllClassesInactive(): void {
    this.filterAll = false;
    this.filterArtificer = false;
    this.filterBard = false;
    this.filterCleric = false;
    this.filterDruid = false;
    this.filterPaladin = false;
    this.filterRanger = false;
    this.filterSorcerer = false;
    this.filterWarlock = false;
    this.filterWizard = false;
    document.getElementById('all')!.classList.remove('icon-active');
    document.getElementById('artificer')!.classList.remove('icon-active');
    document.getElementById('bard')!.classList.remove('icon-active');
    document.getElementById('cleric')!.classList.remove('icon-active');
    document.getElementById('druid')!.classList.remove('icon-active');
    document.getElementById('paladin')!.classList.remove('icon-active');
    document.getElementById('ranger')!.classList.remove('icon-active');
    document.getElementById('sorcerer')!.classList.remove('icon-active');
    document.getElementById('warlock')!.classList.remove('icon-active');
    document.getElementById('wizard')!.classList.remove('icon-active');
  }

  /**
   * Activates all classes that were active before the all classes button was selected.
   */
  private restorePreviousClasses(): void {
    this.previousClasses.forEach((className) => {
      const element = document.getElementById(className);
      element!.classList.toggle('icon-active');
      this.toggleClassFilter(className);
    });
  }

  // CUSTOM SPELLS

  /**
   * In this modal new spells can be created. This can be completely new spells or
   * modified official spells. If successful, the spell is added to the spell list,
   * sent to the daService and sent to the spellsService which in return sends it to the dataBase
   * @param copyOfficalSpell
   * @param spell
   */
  public openSpellCreationModal(
    copyOfficalSpell: boolean,
    spell?: Spell,
  ): void {
    this.modalAccessor.openModal(SpellModalComponent, {
      spell: copyOfficalSpell ? spell : undefined,
      level: this.currentLevel,
      id: this.dataAccessor.customSpellId,
      copyOfficalSpell: copyOfficalSpell,
      classes: [this.dataAccessor.characterData.class],
    });
    const resultSubscription = this.modalAccessor.result$.subscribe(
      (result) => {
        if (result.state === 'add') {
          console.log('Add spell, caught in spellbook component');
          this.dataAccessor.addCustomSpell(result.data);
          this.refreshFilteredSpells();
        }
        resultSubscription.unsubscribe();
      },
    );
  }

  /**
   * Opens the modal to manage custom spells. Here, custom spells can be deleted.
   */
  public openManageCustomSpellsModal(): void {
    this.modalAccessor.openModal(CustomSpellsModalComponent, {
      spells: this.dataAccessor.customSpells,
    });
    const resultSubscription = this.modalAccessor.result$.subscribe(
      (result) => {
        if (result.state === 'delete') {
          result.data.forEach((spell: Spell) => {
            this.dataAccessor.deleteCustomSpell(spell);
            this.dataAccessor.removeFavoriteSpell(spell);
            this.deleteSpellFromPrepared(spell);
          });
          this.refreshFilteredSpells();
        }
        resultSubscription.unsubscribe();
      },
    );
  }

  /**
   * Searches for the level of the given spell and deletes it from the prepared spells.
   * @param spell The spell that is to be deleted
   */
  public deleteSpellFromPrepared(spell: Spell): void {
    if (spell.level === 0) {
      this.dataAccessor.removeSpellFromLevel0(spell);
    } else if (spell.level === 1) {
      this.dataAccessor.removeSpellFromLevel1(spell);
    }
    if (spell.level === 2) {
      this.dataAccessor.removeSpellFromLevel2(spell);
    }
    if (spell.level === 3) {
      this.dataAccessor.removeSpellFromLevel3(spell);
    }
    if (spell.level === 4) {
      this.dataAccessor.removeSpellFromLevel4(spell);
    }
    if (spell.level === 5) {
      this.dataAccessor.removeSpellFromLevel5(spell);
    }
    if (spell.level === 6) {
      this.dataAccessor.removeSpellFromLevel6(spell);
    }
    if (spell.level === 7) {
      this.dataAccessor.removeSpellFromLevel7(spell);
    }
    if (spell.level === 8) {
      this.dataAccessor.removeSpellFromLevel8(spell);
    }
    if (spell.level === 9) {
      this.dataAccessor.removeSpellFromLevel9(spell);
    }
  }

  // SPELLCARDS

  /**
   * Opens the full spellcard modal for a given spell. The spellcard can be used to add the spell to the prepared spells and/or to the favorites.
   * @param spell The spell for which the full spellcard should be opened.
   */
  public showFullSpellcard(spell: Spell): void {
    const favorites = this.dataAccessor.favoriteSpells;
    const alreadyInFavorites = favorites.some(
      (currentSpell) => currentSpell.id === spell.id,
    );
    const alreadyPrepared = this.dataAccessor
      .getAllPreparedSpells()
      .some((currentSpell) => currentSpell.id === spell.id);
    this.modalAccessor.openModal(FullSpellcardComponent, {
      spell: spell,
      origin: 'spellbook',
      alreadyPrepared: alreadyPrepared,
      alreadyInFavorites: alreadyInFavorites,
    });
    // This subscription is used to receive messages from the spellcard without closing the modal.
    const actionSubscription = this.modalAccessor.action$.subscribe(
      (message) => {
        if (message.action === 'addToFavorites') {
          this.dataAccessor.addFavoriteSpell(spell);
        } else if (message.action === 'removeFromFavorites') {
          this.dataAccessor.removeFavoriteSpell(spell);
        } else if (message.action === 'addToPrepared') {
          this.addSpellToPrepared(spell);
        } else if (message.action === 'removeFromPrepared') {
          this.removeSpellFromPrepared(spell);
          this.dataAccessor.removeFavoriteSpell(spell);
        }
      },
    );
    // This subscription is used to receive the result from the spellcard modal.
    const resultSubscription = this.modalAccessor.result$.subscribe(
      (result) => {
        resultSubscription.unsubscribe();
        actionSubscription.unsubscribe();
        if (result.state === 'copy') {
          setTimeout(() => {
            this.openSpellCreationModal(true, spell);
          }, 100);
        } else if (result.state === 'update') {
          setTimeout(() => {
            this.openSpellModificationModal(spell);
          }, 100);
        }
      },
    );
  }

  /**
   * Opens a modal to modify an existing spell. The spell is then updated in the dataAccessor and favorites.
   * @param spell The Spell that is to be modified.
   */
  public openSpellModificationModal(spell: Spell): void {
    this.modalAccessor.openModal(SpellModalComponent, {
      spell: spell,
      isModification: true,
    });
    const resultSubscription = this.modalAccessor.result$.subscribe(
      (result) => {
        if (result.state === 'update') {
          this.dataAccessor.updateCustomSpell(result.data);
          // TODO: Update the spell in the prepared spells and favorite spells
        }
        resultSubscription.unsubscribe();
        this.refreshFilteredSpells();
      },
    );
  }

  /**
   * Adds a given spell to the list of prepared spells. The spell will be added to the corresponding spellArray.
   * Moreover it will also be added to the database in the dataAccessor.
   * @param spell The spell that will be added to its corresponding spellArray.
   */
  private addSpellToPrepared(spell: Spell): void {
    switch (spell.level) {
      case 0:
        this.dataAccessor.addSpellToLevel0(spell);
        break;
      case 1:
        this.dataAccessor.addSpellToLevel1(spell);
        break;
      case 2:
        this.dataAccessor.addSpellToLevel2(spell);
        break;
      case 3:
        this.dataAccessor.addSpellToLevel3(spell);
        break;
      case 4:
        this.dataAccessor.addSpellToLevel4(spell);
        break;
      case 5:
        this.dataAccessor.addSpellToLevel5(spell);
        break;
      case 6:
        this.dataAccessor.addSpellToLevel6(spell);
        break;
      case 7:
        this.dataAccessor.addSpellToLevel7(spell);
        break;
      case 8:
        this.dataAccessor.addSpellToLevel8(spell);
        break;
      case 9:
        this.dataAccessor.addSpellToLevel9(spell);
        break;
    }
  }

  /**
   * Removes a given spell from the list of prepared spells. The spell will be removed from the corresponding spellArray.
   * Moreover it will also be removed from the database in the dataAccessor.
   * @param spell The spell that will be removed from its corresponding spellArray.
   */
  private removeSpellFromPrepared(spell: Spell): void {
    switch (spell.level) {
      case 0:
        this.dataAccessor.removeSpellFromLevel0(spell);
        break;
      case 1:
        this.dataAccessor.removeSpellFromLevel1(spell);
        break;
      case 2:
        this.dataAccessor.removeSpellFromLevel2(spell);
        break;
      case 3:
        this.dataAccessor.removeSpellFromLevel3(spell);
        break;
      case 4:
        this.dataAccessor.removeSpellFromLevel4(spell);
        break;
      case 5:
        this.dataAccessor.removeSpellFromLevel5(spell);
        break;
      case 6:
        this.dataAccessor.removeSpellFromLevel6(spell);
        break;
      case 7:
        this.dataAccessor.removeSpellFromLevel7(spell);
        break;
      case 8:
        this.dataAccessor.removeSpellFromLevel8(spell);
        break;
      case 9:
        this.dataAccessor.removeSpellFromLevel9(spell);
        break;
    }
  }

  /**
   * Returns a list of prepared spells for a given level.
   * @param level The level of the list of prepared spells.
   * @returns Returns the complete list of prepared spells for the given level.
   */
  private getPreparedSpellList(level: number): Spell[] {
    switch (level) {
      case 0:
        return this.dataAccessor.spellLevel0;
      case 1:
        return this.dataAccessor.spellLevel1;
      case 2:
        return this.dataAccessor.spellLevel2;
      case 3:
        return this.dataAccessor.spellLevel3;
      case 4:
        return this.dataAccessor.spellLevel4;
      case 5:
        return this.dataAccessor.spellLevel5;
      case 6:
        return this.dataAccessor.spellLevel6;
      case 7:
        return this.dataAccessor.spellLevel7;
      case 8:
        return this.dataAccessor.spellLevel8;
      case 9:
        return this.dataAccessor.spellLevel9;
      default:
        return [];
    }
  }
}
