import { Injectable } from '@angular/core';
import Localbase from 'localbase';
import { BehaviorSubject } from 'rxjs';
import {
  Weapon,
  Food,
  SimpleItem,
  Trait,
  Ability,
  Spell,
  Skill,
  Attribute,
} from 'src/interfaces/interfaces';
import { SpellsService } from '../spells/spells.service';

@Injectable({
  providedIn: 'root',
})
export class DataService {
  private db: any;

  public dataLoaded = false;
  public characterName: string = '';

  constructor(private spellsService: SpellsService) {
    this.db = new Localbase('DNDTools');
    this.db.config.debug = false;
  }

  async loadData(): Promise<any> {
    this.characterName = sessionStorage.getItem('characterName')!;
    if (this.dataLoaded) {
      return Promise.resolve();
    }
    return this.getCollectionWithKeys(this.characterName).then((data: any) => {
      this.buildCurrentCharacter(data);
      this.dataLoaded = true;
    });
  }

  // #region character selection and construction

  private buildCurrentCharacter(currentCharacterData: any): void {
    const [
      abilitiesData,
      attributesData,
      characterData,
      combatStatsData,
      companionData,
      consumablesData,
      customSpellsData,
      favoriteSpellsData,
      favoriteWeaponsData,
      foodData,
      hitPointsData,
      imageData,
      kiPointsData,
      locationsData,
      mapsData,
      miscellaneousData,
      moneyData,
      notesData,
      npcsData,
      proficienciesData,
      questsData,
      resistancesData,
      skillsData,
      spellLevel0,
      spellLevel1,
      spellLevel2,
      spellLevel3,
      spellLevel4,
      spellLevel5,
      spellLevel6,
      spellLevel7,
      spellLevel8,
      spellLevel9,
      spellslotsData,
      traitsData,
      weaponsAndArmorData,
    ] = currentCharacterData.map((entry: any) => entry.data);

    // Character Data
    this.characterData = characterData;

    //Character Image

    this.image = imageData;

    // Attributes
    Object.keys(attributesData).forEach((key: string) => {
      this.updateAttribute(attributesData[key]);
    });

    //  Skills
    Object.keys(skillsData).forEach((key: string) => {
      this.updateSkill(skillsData[key]);
    });

    // Hit Points
    this.hitPointsSubject.next(hitPointsData.hitPoints);
    this.hitDice = hitPointsData.hitDice;

    // Combat Stats
    this.armorClass = combatStatsData.armorClass;
    this.initiative = combatStatsData.initiative;
    this.movement = combatStatsData.movement;
    this.updateProficiencyBonus(combatStatsData.proficiencyBonus);
    this.deathSaves = combatStatsData.deathSaves;
    this.conditions = combatStatsData.conditions;
    this.exhaustion = combatStatsData.exhaustion;
    this.inspiration = combatStatsData.inspiration;

    // Spells

    this.customSpellId = customSpellsData.id;
    this.customSpells = customSpellsData.spells;
    this.spellslots = spellslotsData;
    this.favoriteSpells = favoriteSpellsData.spells;
    this.spellLevel0 = spellLevel0.spells;
    this.spellLevel1 = spellLevel1.spells;
    this.spellLevel2 = spellLevel2.spells;
    this.spellLevel3 = spellLevel3.spells;
    this.spellLevel4 = spellLevel4.spells;
    this.spellLevel5 = spellLevel5.spells;
    this.spellLevel6 = spellLevel6.spells;
    this.spellLevel7 = spellLevel7.spells;
    this.spellLevel8 = spellLevel8.spells;
    this.spellLevel9 = spellLevel9.spells;

    // Upon initialization of the data, the custom spells need to be added to the spellsService

    this.spellsService.customSpells = this.customSpells;

    // Items
    this.favoriteWeapons = favoriteWeaponsData.data;
    this.weaponsAndArmor = weaponsAndArmorData.data;
    this.consumables = consumablesData.data;
    this.miscellaneous = miscellaneousData.data;
    this.food = foodData.data;
    this.money = moneyData;

    // Abilities and stuff
    this.kiPoints = kiPointsData;
    this.traits = traitsData.data;
    this.abilities = abilitiesData.data;
    this.proficiencies = proficienciesData;
  }

  // #endregion

  //////////////////////
  // FUTURE DATA  //////
  //////////////////////

  private _companions: any = {};
  private _notes: any = {};
  private _quests: any = {};
  private _npcs: any = {};
  private _locations: any = {};
  private _maps: any = {};
  private _resistances: any = {};

  public get resistances(): any {
    return this._resistances;
  }

  public set resistances(resistances: any) {
    this._resistances = resistances;
    this.setData('resistances', resistances);
  }
  // #region # Character and Image DATA

  private _image: any = {
    value: '',
  };

  public get image(): any {
    return this._image;
  }

  public set image(newValue: any) {
    this._image = newValue;
    this.setData('image', newValue);
  }

  private _characterData: any = {
    class: '',
    subclass: '',
    race: '',
    background: '',
    backgroundStory: '',
    level: 1,
    experience: 0,
  };

  public get characterData(): any {
    return this._characterData;
  }

  public set characterData(newValue: any) {
    this._characterData = newValue;
    this.setData('characterData', newValue);
  }

  // #endregion

  // #region # SPELLS

  private _favoriteSpells: Spell[] = [];

  public get favoriteSpells(): Spell[] {
    return this._favoriteSpells;
  }

  public set favoriteSpells(spells: Spell[]) {
    this._favoriteSpells = spells;
    this.setData('favoriteSpells', { spells: spells });
  }

  public addFavoriteSpell(spell: Spell): void {
    this._favoriteSpells.push(spell);
    this.setData('favoriteSpells', { spells: this._favoriteSpells });
  }

  /**
   * Checks if the given spell is in the favorite spells array.
   * If so, it is matched by the id and updated.
   * @param spell The spell to be updated.
   */
  public updateFavoriteSpell(spell: Spell): void {
    const index = this._favoriteSpells.findIndex(
      (favorite) => favorite.id === spell.id,
    );
    if (index > -1) {
      this._favoriteSpells[index] = spell;
      this.setData('favoriteSpells', { spells: this._favoriteSpells });
    }
  }

  public removeFavoriteSpell(spell: Spell): void {
    const index = this._favoriteSpells.findIndex(
      (favorite) => favorite.id === spell.id,
    );
    if (index > -1) {
      this._favoriteSpells.splice(index, 1);
      this.setData('favoriteSpells', { spells: this._favoriteSpells });
    }
  }

  private _customSpells: Spell[] = [];

  public get customSpells(): Spell[] {
    return this._customSpells;
  }

  public set customSpells(spells: Spell[]) {
    this._customSpells = spells;
    this.setData('customSpells', { spells: spells, id: this.customSpellId });
  }

  /**
   * Adds a custom spell to the data service and uploads the resulting array to the database.
   * Additionally, the custom spell ID is incremented by one.
   * The custom spell array in the spells service is also updated.
   * @param spell The spell to be added.
   */
  public addCustomSpell(spell: Spell): void {
    this._customSpells.push(spell);
    this._customSpellId++;
    this.spellsService.customSpells = this._customSpells;
    this.setData('customSpells', {
      spells: this._customSpells,
      id: this.customSpellId,
    });
  }

  /**
   * Updates a custom spell in the data service and uploads the resulting array to the database.
   * Moreover, the custom spell array in the spells service is also updated.
   * @param spell The spell to be updated.
   */
  public updateCustomSpell(spell: Spell): void {
    const index = this._customSpells.findIndex(
      (customSpell) => customSpell.id === spell.id,
    );
    this._customSpells[index] = spell;
    this.spellsService.customSpells = this._customSpells;
    this.setData('customSpells', {
      spells: this._customSpells,
      id: this.customSpellId,
    });
  }

  /**
   * Deletes a custom spell from the data service and uploads the resulting array to the database.
   * @param spell The spell to be deleted.
   */
  public deleteCustomSpell(spell: Spell): void {
    const index = this._customSpells.indexOf(spell);
    this._customSpells.splice(index, 1);
    this.setData('customSpells', {
      spells: this._customSpells,
      id: this.customSpellId,
    });
  }

  public deleteCustomSpellArray(spells: Spell[]): void {
    spells.forEach((spell) => {
      const index = this._customSpells.indexOf(spell);
      this._customSpells.splice(index, 1);
    });
    this.setData('customSpells', {
      spells: this._customSpells,
      id: this.customSpellId,
    });
  }

  private _customSpellId: number = 10000;

  public get customSpellId(): number {
    return this._customSpellId;
  }

  private set customSpellId(id: number) {
    this._customSpellId = id;
  }
  // LEVEL 0

  private _spellLevel0: Spell[] = [];

  public get spellLevel0(): Spell[] {
    return this._spellLevel0;
  }

  public set spellLevel0(spells: Spell[]) {
    this._spellLevel0 = spells;
    this.setData('spellLevel0', { spells: spells });
  }

  public addSpellToLevel0(spell: Spell): void {
    this._spellLevel0.push(spell);
    this.setData('spellLevel0', { spells: this._spellLevel0 });
  }

  public removeSpellFromLevel0(spell: Spell): void {
    const index = this._spellLevel0.findIndex((obj) => obj.id === spell.id);
    this._spellLevel0.splice(index, 1);
    this.setData('spellLevel0', { spells: this._spellLevel0 });
  }

  // LEVEL 1
  private _spellLevel1: Spell[] = [];

  public get spellLevel1(): Spell[] {
    return this._spellLevel1;
  }

  public set spellLevel1(spells: Spell[]) {
    this._spellLevel1 = spells;
    this.setData('spellLevel1', { spells: spells });
  }

  public addSpellToLevel1(spell: Spell): void {
    this._spellLevel1.push(spell);
    this.setData('spellLevel1', { spells: this._spellLevel1 });
  }

  public removeSpellFromLevel1(spell: Spell): void {
    const index = this._spellLevel1.findIndex((obj) => obj.id === spell.id);
    this._spellLevel1.splice(index, 1);
    this.setData('spellLevel1', { spells: this._spellLevel1 });
  }

  // LEVEL 2

  private _spellLevel2: Spell[] = [];

  public get spellLevel2(): Spell[] {
    return this._spellLevel2;
  }

  public set spellLevel2(spells: Spell[]) {
    this._spellLevel2 = spells;
    this.setData('spellLevel2', { spells: spells });
  }

  public addSpellToLevel2(spell: Spell): void {
    this._spellLevel2.push(spell);
    this.setData('spellLevel2', { spells: this._spellLevel2 });
  }

  public removeSpellFromLevel2(spell: Spell): void {
    const index = this._spellLevel2.findIndex((obj) => obj.id === spell.id);
    this._spellLevel2.splice(index, 1);
    this.setData('spellLevel2', { spells: this._spellLevel2 });
  }

  // LEVEL 3

  private _spellLevel3: Spell[] = [];

  public get spellLevel3(): Spell[] {
    return this._spellLevel3;
  }

  public set spellLevel3(spells: Spell[]) {
    this._spellLevel3 = spells;
    this.setData('spellLevel3', { spells: spells });
  }

  public addSpellToLevel3(spell: Spell): void {
    this._spellLevel3.push(spell);
    this.setData('spellLevel3', { spells: this._spellLevel3 });
  }

  public removeSpellFromLevel3(spell: Spell): void {
    const index = this._spellLevel3.findIndex((obj) => obj.id === spell.id);
    this._spellLevel3.splice(index, 1);
    this.setData('spellLevel3', { spells: this._spellLevel3 });
  }

  // LEVEL 4

  private _spellLevel4: Spell[] = [];

  public get spellLevel4(): Spell[] {
    return this._spellLevel4;
  }

  public set spellLevel4(spells: Spell[]) {
    this._spellLevel4 = spells;
    this.setData('spellLevel4', { spells: spells });
  }

  public addSpellToLevel4(spell: Spell): void {
    this._spellLevel4.push(spell);
    this.setData('spellLevel4', { spells: this._spellLevel4 });
  }

  public removeSpellFromLevel4(spell: Spell): void {
    const index = this._spellLevel4.findIndex((obj) => obj.id === spell.id);
    this._spellLevel4.splice(index, 1);
    this.setData('spellLevel4', { spells: this._spellLevel4 });
  }

  // LEVEL 5
  private _spellLevel5: Spell[] = [];

  public get spellLevel5(): Spell[] {
    return this._spellLevel5;
  }

  public set spellLevel5(spells: Spell[]) {
    this._spellLevel5 = spells;
    this.setData('spellLevel5', { spells: spells });
  }

  public addSpellToLevel5(spell: Spell): void {
    this._spellLevel5.push(spell);
    this.setData('spellLevel5', { spells: this._spellLevel5 });
  }

  public removeSpellFromLevel5(spell: Spell): void {
    const index = this._spellLevel5.findIndex((obj) => obj.id === spell.id);
    this._spellLevel5.splice(index, 1);
    this.setData('spellLevel5', { spells: this._spellLevel5 });
  }

  // LEVEL 6

  private _spellLevel6: Spell[] = [];

  public get spellLevel6(): Spell[] {
    return this._spellLevel6;
  }

  public set spellLevel6(spells: Spell[]) {
    this._spellLevel6 = spells;
    this.setData('spellLevel6', { spells: spells });
  }

  public addSpellToLevel6(spell: Spell): void {
    this._spellLevel6.push(spell);
    this.setData('spellLevel6', { spells: this._spellLevel6 });
  }

  public removeSpellFromLevel6(spell: Spell): void {
    const index = this._spellLevel6.findIndex((obj) => obj.id === spell.id);
    this._spellLevel6.splice(index, 1);
    this.setData('spellLevel6', { spells: this._spellLevel6 });
  }

  // LEVEL 7
  private _spellLevel7: Spell[] = [];

  public get spellLevel7(): Spell[] {
    return this._spellLevel7;
  }

  public set spellLevel7(spells: Spell[]) {
    this._spellLevel7 = spells;
    this.setData('spellLevel7', { spells: spells });
  }

  public addSpellToLevel7(spell: Spell): void {
    this._spellLevel7.push(spell);
    this.setData('spellLevel7', { spells: this._spellLevel7 });
  }

  public removeSpellFromLevel7(spell: Spell): void {
    const index = this._spellLevel7.findIndex((obj) => obj.id === spell.id);
    this._spellLevel7.splice(index, 1);
    this.setData('spellLevel7', { spells: this._spellLevel7 });
  }

  // LEVEL 8

  private _spellLevel8: Spell[] = [];

  public get spellLevel8(): Spell[] {
    return this._spellLevel8;
  }

  public set spellLevel8(spells: Spell[]) {
    this._spellLevel8 = spells;
    this.setData('spellLevel8', { spells: spells });
  }

  public addSpellToLevel8(spell: Spell): void {
    this._spellLevel8.push(spell);
    this.setData('spellLevel8', { spells: this._spellLevel8 });
  }

  public removeSpellFromLevel8(spell: Spell): void {
    const index = this._spellLevel8.findIndex((obj) => obj.id === spell.id);
    this._spellLevel8.splice(index, 1);
    this.setData('spellLevel8', { spells: this._spellLevel8 });
  }

  // LEVEL 9

  private _spellLevel9: Spell[] = [];

  public get spellLevel9(): Spell[] {
    return this._spellLevel9;
  }

  public set spellLevel9(spells: Spell[]) {
    this._spellLevel9 = spells;
    this.setData('spellLevel9', { spells: spells });
  }

  public addSpellToLevel9(spell: Spell): void {
    this._spellLevel9.push(spell);
    this.setData('spellLevel9', { spells: this._spellLevel9 });
  }

  public removeSpellFromLevel9(spell: Spell): void {
    const index = this._spellLevel9.findIndex((obj) => obj.id === spell.id);
    this._spellLevel9.splice(index, 1);
    this.setData('spellLevel9', { spells: this._spellLevel9 });
  }

  public getAllPreparedSpells(): Spell[] {
    return [
      ...this._spellLevel0,
      ...this._spellLevel1,
      ...this._spellLevel2,
      ...this._spellLevel3,
      ...this._spellLevel4,
      ...this._spellLevel5,
      ...this._spellLevel6,
      ...this._spellLevel7,
      ...this._spellLevel8,
      ...this._spellLevel9,
    ];
  }

  // #endregion

  // #region # ABILITIES

  private _abilities: Ability[] = [];

  public get abilities(): Ability[] {
    return this._abilities;
  }

  public set abilities(abilities: Ability[]) {
    this._abilities = abilities;
    this.setData('abilities', { data: abilities });
  }

  private _traits: Trait[] = [];

  public get traits(): Trait[] {
    return this._traits;
  }

  public set traits(traits: Trait[]) {
    this._traits = traits;
    this.setData('traits', { data: traits });
  }

  private _spellslots: any = {
    spellslots: [],
    showSpellslots: true,
    spellcastingAttribute: undefined,
  };

  public get spellslots(): any {
    return this._spellslots;
  }

  public set spellslots(spellslots: any) {
    this._spellslots = spellslots;
    this.setData('spellslots', spellslots);
  }

  private _kiPoints: any = {
    totalPoints: 4,
    usedPoints: 2,
    showKiPoints: false,
  };

  public get kiPoints(): any {
    return this._kiPoints;
  }

  public set kiPoints(kiPoints: any) {
    this._kiPoints = kiPoints;
    this.setData('kiPoints', kiPoints);
  }

  private _proficiencies: any = {};

  public get proficiencies(): any {
    return this._proficiencies;
  }

  public set proficiencies(proficiencies: any) {
    this._proficiencies = proficiencies;
    this.setData('proficiencies', proficiencies);
  }

  // #endregion

  // #region # COMBAT STATS

  private proficiencySubject = new BehaviorSubject<number>(2);
  public proficiency$ = this.proficiencySubject.asObservable();

  public updateProficiencyBonus(newValue: number) {
    this.proficiencySubject.next(newValue);
    this.writeCombatStatsToDatabase();
  }

  private _armorClass: number = 10;

  public get armorClass(): number {
    return this._armorClass;
  }

  public set armorClass(newValue: number) {
    this._armorClass = newValue;
    this.writeCombatStatsToDatabase();
  }

  private _initiative: number = 0;

  public get initiative(): number {
    return this._initiative;
  }

  public set initiative(newValue: number) {
    this._initiative = newValue;
    this.writeCombatStatsToDatabase();
  }

  private _movement: number = 30;

  public get movement(): number {
    return this._movement;
  }

  public set movement(newValue: number) {
    this._movement = newValue;
    this.writeCombatStatsToDatabase();
  }

  private _deathSaves: number[] = [0, 0];

  public get deathSaves(): number[] {
    return this._deathSaves;
  }

  public set deathSaves(newValue: number[]) {
    this._deathSaves = newValue;
    this.writeCombatStatsToDatabase();
  }

  private _inspiration: boolean = false;

  public get inspiration(): boolean {
    return this._inspiration;
  }

  public set inspiration(newValue: boolean) {
    this._inspiration = newValue;
    this.writeCombatStatsToDatabase();
  }

  private _experience: number = 0;

  public get experience(): number {
    return this._experience;
  }

  public set experience(newValue: number) {
    this._experience = newValue;
    this.writeCombatStatsToDatabase();
  }

  private _exhaustion: number = 0;

  public get exhaustion(): number {
    return this._exhaustion;
  }

  public set exhaustion(newValue: number) {
    this._exhaustion = newValue;
    this.writeCombatStatsToDatabase();
  }

  private _conditions: string[] = [];

  public get conditions(): string[] {
    return this._conditions;
  }

  public set conditions(newValue: string[]) {
    this._conditions = newValue;
    this.writeCombatStatsToDatabase();
  }

  private writeCombatStatsToDatabase() {
    const combatStatsData = {
      armorClass: this._armorClass,
      initiative: this._initiative,
      movement: this._movement,
      deathSaves: this._deathSaves,
      proficiencyBonus: this.proficiencySubject.getValue(),
      inspiration: this._inspiration,
      experience: this._experience,
      exhaustion: this._exhaustion,
      conditions: this._conditions,
    };
    this.setData('combatStats', combatStatsData);
  }

  // #endregion

  // #region # HIT POINTS

  private hitPointsSubject = new BehaviorSubject<any>({
    maxHitPoints: 6,
    currentHitPoints: 6,
    temporaryHitPoints: 1,
  });
  public hitPoints$ = this.hitPointsSubject.asObservable();

  public updateHitPoints(newValue: any) {
    this.hitPointsSubject.next(newValue);
    this.updateLifeData();
  }

  private _hitDice = {
    diceNumber: 4,
    diceType: 10,
    diceUsed: 0,
  };

  public get hitDice(): any {
    return this._hitDice;
  }

  public set hitDice(newValue: any) {
    this._hitDice = newValue;
    this.updateLifeData();
  }

  private updateLifeData() {
    const hitPointsData = {
      hitPoints: this.hitPointsSubject.getValue(),
      hitDice: this._hitDice,
    };
    this.setData('hitPoints', hitPointsData);
  }

  // #endregion

  // #region # ATTRIBUTES
  updateAttribute(newValue: Attribute) {
    const functionCall: string = newValue.name + 'Subject.next(newValue)';
    eval(`this.${functionCall}`);
    const attributesData = {
      strength: this.strengthSubject.getValue(),
      dexterity: this.dexteritySubject.getValue(),
      constitution: this.constitutionSubject.getValue(),
      intelligence: this.intelligenceSubject.getValue(),
      wisdom: this.wisdomSubject.getValue(),
      charisma: this.charismaSubject.getValue(),
    };
    this.setData('attributes', attributesData);
  }

  private strengthSubject = new BehaviorSubject<Attribute>({
    name: 'strength',
    value: 10,
    proficiency: true,
  });
  public strength$ = this.strengthSubject.asObservable();

  private dexteritySubject = new BehaviorSubject<Attribute>({
    name: 'dexterity',
    value: 10,
    proficiency: false,
  });
  public dexterity$ = this.dexteritySubject.asObservable();

  private constitutionSubject = new BehaviorSubject<Attribute>({
    name: 'constitution',
    value: 10,
    proficiency: false,
  });
  public constitution$ = this.constitutionSubject.asObservable();

  private intelligenceSubject = new BehaviorSubject<Attribute>({
    name: 'intelligence',
    value: 10,
    proficiency: false,
  });
  public intelligence$ = this.intelligenceSubject.asObservable();

  private wisdomSubject = new BehaviorSubject<Attribute>({
    name: 'wisdom',
    value: 10,
    proficiency: true,
  });
  public wisdom$ = this.wisdomSubject.asObservable();

  private charismaSubject = new BehaviorSubject<Attribute>({
    name: 'charisma',
    value: 10,
    proficiency: false,
  });
  public charisma$ = this.charismaSubject.asObservable();

  // #endregion

  // #region # SKILLS

  updateSkill(newValue: Skill) {
    const functionCall: string = newValue.name + 'Subject.next(newValue)';
    eval(`this.${functionCall}`);
    const skillsData = {
      acrobatics: this.acrobaticsSubject.getValue(),
      animalHandling: this.animalHandlingSubject.getValue(),
      arcana: this.arcanaSubject.getValue(),
      athletics: this.athleticsSubject.getValue(),
      deception: this.deceptionSubject.getValue(),
      history: this.historySubject.getValue(),
      insight: this.insightSubject.getValue(),
      intimidation: this.intimidationSubject.getValue(),
      investigation: this.investigationSubject.getValue(),
      medicine: this.medicineSubject.getValue(),
      nature: this.natureSubject.getValue(),
      perception: this.perceptionSubject.getValue(),
      performance: this.performanceSubject.getValue(),
      persuasion: this.persuasionSubject.getValue(),
      religion: this.religionSubject.getValue(),
      sleightOfHand: this.sleightOfHandSubject.getValue(),
      stealth: this.stealthSubject.getValue(),
      survival: this.survivalSubject.getValue(),
    };
    this.setData('skills', skillsData);
  }

  private acrobaticsSubject = new BehaviorSubject<Skill>({
    name: 'acrobatics',
    proficiency: true,
  });
  public acrobatics$ = this.acrobaticsSubject.asObservable();

  private animalHandlingSubject = new BehaviorSubject<Skill>({
    name: 'animalHandling',
    proficiency: false,
  });
  public animalHandling$ = this.animalHandlingSubject.asObservable();

  private arcanaSubject = new BehaviorSubject<Skill>({
    name: 'arcana',
    proficiency: false,
  });
  public arcana$ = this.arcanaSubject.asObservable();

  private athleticsSubject = new BehaviorSubject<Skill>({
    name: 'athletics',
    proficiency: true,
  });
  public athletics$ = this.athleticsSubject.asObservable();

  private deceptionSubject = new BehaviorSubject<Skill>({
    name: 'deception',
    proficiency: false,
  });
  public deception$ = this.deceptionSubject.asObservable();

  private historySubject = new BehaviorSubject<Skill>({
    name: 'history',
    proficiency: false,
  });
  public history$ = this.historySubject.asObservable();

  private insightSubject = new BehaviorSubject<Skill>({
    name: 'insight',
    proficiency: false,
  });
  public insight$ = this.insightSubject.asObservable();

  private intimidationSubject = new BehaviorSubject<Skill>({
    name: 'intimidation',
    proficiency: false,
  });
  public intimidation$ = this.intimidationSubject.asObservable();

  private investigationSubject = new BehaviorSubject<Skill>({
    name: 'investigation',
    proficiency: false,
  });
  public investigation$ = this.investigationSubject.asObservable();

  private medicineSubject = new BehaviorSubject<Skill>({
    name: 'medicine',
    proficiency: false,
  });
  public medicine$ = this.medicineSubject.asObservable();

  private natureSubject = new BehaviorSubject<Skill>({
    name: 'nature',
    proficiency: false,
  });
  public nature$ = this.natureSubject.asObservable();

  private perceptionSubject = new BehaviorSubject<Skill>({
    name: 'perception',
    proficiency: false,
  });
  public perception$ = this.perceptionSubject.asObservable();

  private performanceSubject = new BehaviorSubject<Skill>({
    name: 'performance',
    proficiency: false,
  });
  public performance$ = this.performanceSubject.asObservable();

  private persuasionSubject = new BehaviorSubject<Skill>({
    name: 'persuasion',
    proficiency: false,
  });
  public persuasion$ = this.persuasionSubject.asObservable();

  private religionSubject = new BehaviorSubject<Skill>({
    name: 'religion',
    proficiency: false,
  });
  public religion$ = this.religionSubject.asObservable();

  private sleightOfHandSubject = new BehaviorSubject<Skill>({
    name: 'sleightOfHand',
    proficiency: false,
  });
  public sleightOfHand$ = this.sleightOfHandSubject.asObservable();

  private stealthSubject = new BehaviorSubject<Skill>({
    name: 'stealth',
    proficiency: false,
  });
  public stealth$ = this.stealthSubject.asObservable();

  private survivalSubject = new BehaviorSubject<Skill>({
    name: 'survival',
    proficiency: false,
  });
  public survival$ = this.survivalSubject.asObservable();

  // #endregion

  // #region # inventory

  private _favoriteWeapons: Weapon[] = [];

  public get favoriteWeapons(): Weapon[] {
    return this._favoriteWeapons;
  }

  public set favoriteWeapons(weapons: Weapon[]) {
    this._favoriteWeapons = weapons;
    this.setData('favoriteWeapons', { data: this._favoriteWeapons });
  }

  private _weaponsAndArmor: SimpleItem[] = [];

  public get weaponsAndArmor(): SimpleItem[] {
    return this._weaponsAndArmor;
  }

  public set weaponsAndArmor(newValue: SimpleItem[]) {
    this._weaponsAndArmor = newValue;
    this.setData('weaponsAndArmor', { data: this._weaponsAndArmor });
  }

  private _food: Food[] = [];

  public get food(): Food[] {
    return this._food;
  }

  public set food(newValue: Food[]) {
    this._food = newValue;
    this.setData('food', { data: this._food });
  }

  private _money: any = {
    copper: 100,
    silver: 100,
    electrum: 100,
    gold: 100,
    platinum: 100,
  };

  public get money(): any {
    return this._money;
  }

  public set money(newValue: any) {
    this._money = newValue;
    this.setData('money', this._money);
  }

  private _consumbales: SimpleItem[] = [];

  public get consumables(): SimpleItem[] {
    return this._consumbales;
  }

  public set consumables(newValue: SimpleItem[]) {
    this._consumbales = newValue;
    this.setData('consumables', { data: this._consumbales });
  }

  private _miscellaneous: SimpleItem[] = [];

  public get miscellaneous(): SimpleItem[] {
    return this._miscellaneous;
  }

  public set miscellaneous(newValue: SimpleItem[]) {
    this._miscellaneous = newValue;
    this.setData('miscellaneous', { data: this._miscellaneous });
  }

  // #endregion

  // #region database calls

  public async addData(
    collection: string,
    data: any,
    key?: string,
  ): Promise<void> {
    if (key) {
      return this.db
        .collection(collection)
        .add(data, key)
        .then(() => {});
    } else {
      return this.db
        .collection(collection)
        .add(data)
        .then(() => {});
    }
  }

  public getData(collection: string, key: string) {
    return this.db.collection(collection).doc(key).get();
  }

  public getCollection(collection: string) {
    return this.db.collection(collection).get();
  }

  public getCollectionWithKeys(collection: string) {
    return this.db.collection(collection).get({ keys: true });
  }

  public setCollection(collection: string, data: any) {
    return this.db.collection(collection).set(data);
  }

  public setData(key: string, data: any) {
    return this.db
      .collection(this.characterName)
      .doc(key)
      .set(data)
      .then(() => {});
  }

  public deleteCollection(collection: string) {
    console.log('delete collection', collection);

    this.db.collection(collection).delete();
  }

  // #endregion
}
