import { Injectable } from '@angular/core';
import Localbase from 'localbase';
import { Character } from 'src/interfaces/character';
import { Router } from '@angular/router';
import { BehaviorSubject } from 'rxjs';
import { Attribute } from 'src/interfaces/attribute';
import { Skill } from 'src/interfaces/skill';
import { Weapon } from 'src/interfaces/weapon';
import { Spell } from 'src/interfaces/spell';

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

  constructor(private Router: Router) {
    this.db = new Localbase('myDatabase');
    // this.test();
  }

  // public test(): void {
  //   let char: Character = {
  //     characterData: {
  //       name: 'Eliane',
  //       class: '',
  //       race: '',
  //       level: '',
  //       subclass: '',
  //       background: '',
  //       experience: '',
  //       inspiration: '',
  //       proficiencyBonus: '2',
  //     },
  //     attributes: {
  //       strength: ['12', false],
  //       dexterity: ['14', false],
  //       constitution: ['16', false],
  //       intelligence: ['10', false],
  //       wisdom: ['16', false],
  //       charisma: ['10', false],
  //     },
  //     skills: {
  //       acrobatics: ['12', false],
  //       animalHandling: ['11', false],
  //       arcana: ['11', false],
  //       athletics: ['11', false],
  //       deception: ['11', false],
  //       history: ['12', false],
  //       insight: ['13', false],
  //       intimidation: ['14', false],
  //       investigation: ['16', false],
  //       medicine: ['15', false],
  //       nature: ['17', false],
  //       perception: ['18', false],
  //       performance: ['19', false],
  //       persuasion: ['20', false],
  //       religion: ['9', false],
  //       sleightOfHand: ['8', false],
  //       stealth: ['7', false],
  //       survival: ['6', false],
  //     },
  //     combatStats: {
  //       armorClass: '18',
  //       initiative: '+2',
  //       speed: '30',
  //       hitPointMaximum: '12',
  //       currentHitPoints: '12',
  //       temporaryHitPoints: '0',
  //       hitDice: '',
  //       deathSaveSuccesses: '0',
  //       deathSaveFailures: '0',
  //     },
  //     appearance: {
  //       age: '',
  //       height: '',
  //       weight: '',
  //       eyes: '',
  //       skin: '',
  //       hair: '',
  //     },
  //     personality: {
  //       personalityTraits: '',
  //       ideals: '',
  //       bonds: '',
  //       flaws: '',
  //     },
  //     weapons: {
  //       name: '',
  //       attackBonus: '',
  //       damage: '',
  //       damageType: '',
  //       range: '',
  //       description: '',
  //     },
  //   };
  //   this.character = char;
  //   // console.log('character: ', this.character);
  // }

  public character: any = {};

  // #region character data retrieval

  public getCompleteCharacter(): Character {
    return this.character;
  }

  public getCharacterData(): any {
    return this.character.characterData;
  }

  public getAppearance(): any {
    return this.character.appearance;
  }

  public getAttributes(): any {
    return this.character.attributes;
  }

  public getSkills(): any {
    return this.character.skills;
  }

  public getCombatStats(): any {
    return this.character.combatStats;
  }

  public getPersonality(): any {
    return this.character.personality;
  }

  // public getWeapons(): any {
  //   return this.character.weapons;
  // }

  // #endregion

  // #region data storage

  private weapons: Weapon[] = [
    {
      name: 'Großschwert',
      attackBonus: '+3',
      damage: [{ damage: '2d6+1', damageType: 'slashing' }],
      proficient: true,
      range: '5ft',
      isFinesse: false,
      isVersatile: false,
      isTwoHanded: true,
      isRanged: false,
    },
    {
      name: 'Kurzbogen',
      attackBonus: '+5',
      damage: [{ damage: '1d6+3', damageType: 'piercing' }],
      proficient: false,
      range: '80/320ft',
      isFinesse: false,
      isVersatile: false,
      isTwoHanded: true,
      isRanged: true,
    },
    {
      name: 'Feuerstab',
      attackBonus: '+5',
      damage: [
        { damage: '1d6+3', damageType: 'bludgeoning' },
        { damage: '1d4', damageType: 'fire' },
      ],
      versatileDamage: [
        { damage: '1d8+3', damageType: 'bludgeoning' },
        { damage: '1d4', damageType: 'fire' },
      ],
      proficient: true,
      range: '5ft',
      isFinesse: false,
      isVersatile: true,
      isTwoHanded: false,
      isRanged: false,
    },
  ];

  public getWeapons(): Weapon[] {
    return this.weapons;
  }
  public setWeapons(weapons: Weapon[]): void {
    this.weapons = weapons;
    console.log('weapons updated: ', this.weapons);
  }

  private spells: Spell[] = [
    {
      name: 'Feuerball',
      attackBonus: '+5',
      damage: '8d6',
      type: 'Feuer',
      range: '150ft',
      level: 3,
    },
    {
      name: 'Magische Geschosse',
      attackBonus: '+5',
      damage: '1d4+1',
      type: 'Magisch',
      range: '120ft',
      level: 1,
    },
    {
      name: 'Flammenpfeil',
      attackBonus: '+5',
      damage: '1d6+3',
      type: 'Feuer',
      range: '90ft',
      level: 2,
    },
  ];

  public getSpells(): Spell[] {
    return this.spells;
  }

  public setSpells(spells: Spell[]): void {
    this.spells = spells;
  }

  // #region Character Data observables

  /**
   *
   * @param newValue The new value of the Character Data
   */
  public updateCharacterData(newValue: any) {
    const functionCall: string = newValue.name + 'Subject.next(newValue)';
    eval(`this.${functionCall}`);
    console.log('updateProperty: ', newValue.value);
    // TODO: Datenbank updaten
  }

  private armorClassSubject = new BehaviorSubject<object>({
    name: 'armorClass',
    value: 19,
  });
  public armorClass$ = this.armorClassSubject.asObservable();

  private initiativeSubject = new BehaviorSubject<object>({
    name: 'initiative',
    value: 2,
  });
  public initiative$ = this.initiativeSubject.asObservable();

  private speedSubject = new BehaviorSubject<object>({
    name: 'speed',
    value: 30,
  });
  public speed$ = this.speedSubject.asObservable();

  private hitPointMaximumSubject = new BehaviorSubject<object>({
    name: 'hitPointMaximum',
    value: 10,
  });
  public hitPointMaximum$ = this.hitPointMaximumSubject.asObservable();

  private currentHitPointsSubject = new BehaviorSubject<object>({
    name: 'currentHitPoints',
    value: 5,
  });
  public currentHitPoints$ = this.currentHitPointsSubject.asObservable();

  private temporaryHitPointsSubject = new BehaviorSubject<object>({
    name: 'temporaryHitPoints',
    value: 3,
  });
  public temporaryHitPoints$ = this.temporaryHitPointsSubject.asObservable();

  private hitDiceSubject = new BehaviorSubject<object>({
    name: 'hitDice',
    value: '',
  });
  public hitDice$ = this.hitDiceSubject.asObservable();

  private deathSaveSubject = new BehaviorSubject<object>({
    name: 'deathSave',
    value: [0, 0],
  });
  public deathSave$ = this.deathSaveSubject.asObservable();

  private inspirationSubject = new BehaviorSubject<object>({
    name: 'inspiration',
    value: 0,
  });
  public inspiration$ = this.inspirationSubject.asObservable();

  private proficiencySubject = new BehaviorSubject<object>({
    name: 'proficiencyBonus',
    value: 2,
  });
  public proficiency$ = this.proficiencySubject.asObservable();

  private experienceSubject = new BehaviorSubject<object>({
    name: 'experience',
    value: 0,
  });
  public experience$ = this.experienceSubject.asObservable();

  private exhaustionSubject = new BehaviorSubject<object>({
    name: 'exhaustion',
    value: 0,
  });
  public exhaustion$ = this.exhaustionSubject.asObservable();

  // #endregion

  // #region Weapons and Items observables

  private weaponsSubject = new BehaviorSubject<object>({
    name: 'weapons',
    value: [{ name: '', attackBonus: '', damage: '', damageType: '' }],
  });
  public weapons$ = this.experienceSubject.asObservable();

  private spellsSubject = new BehaviorSubject<object>({
    name: 'spells',
    value: [{ name: '', attackBonus: '', damage: '', damageType: '' }],
  });
  public spells$ = this.experienceSubject.asObservable();

  // #region Attribute observables

  /**
   *
   * @param newValue The new value of the Attribute
   */
  updateAttribute(newValue: Attribute) {
    const functionCall: string = newValue.name + 'Subject.next(newValue)';
    eval(`this.${functionCall}`);
    console.log('updateAttribute: ', newValue.name);
    // TODO: Datenbank updaten
  }

  private strengthSubject = new BehaviorSubject<Attribute>(
    { name: 'strength', value: 10, proficiency: true } //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
  );
  public strength$ = this.strengthSubject.asObservable();

  private dexteritySubject = new BehaviorSubject<Attribute>(
    { name: 'dexterity', value: 17, proficiency: false } //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
  );
  public dexterity$ = this.dexteritySubject.asObservable();

  private constitutionSubject = new BehaviorSubject<Attribute>(
    { name: 'constitution', value: 10, proficiency: false } //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
  );
  public constitution$ = this.constitutionSubject.asObservable();

  private intelligenceSubject = new BehaviorSubject<Attribute>(
    { name: 'intelligence', value: 13, proficiency: false } //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
  );
  public intelligence$ = this.intelligenceSubject.asObservable();

  private wisdomSubject = new BehaviorSubject<Attribute>(
    { name: 'wisdom', value: 10, proficiency: true } //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
  );
  public wisdom$ = this.wisdomSubject.asObservable();

  private charismaSubject = new BehaviorSubject<Attribute>(
    { name: 'charisma', value: 19, proficiency: false } //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
  );
  public charisma$ = this.charismaSubject.asObservable();

  // #endregion

  // #region Skill observables

  /**
   *
   * @param newValue The new value of the skill
   */
  updateSkill(newValue: Skill) {
    const functionCall: string = newValue.name + 'Subject.next(newValue)';
    eval(`this.${functionCall}`);
    console.log('updateSkill: ', newValue.name);
    // TODO: Datenbank updaten
  }

  private acrobaticsSubject = new BehaviorSubject<Skill>(
    { name: 'acrobatics', proficiency: true } //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
  );
  public acrobatics$ = this.acrobaticsSubject.asObservable();

  private animalHandlingSubject = new BehaviorSubject<Skill>(
    { name: 'animalHandling', proficiency: false } //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
  );
  public animalHandling$ = this.animalHandlingSubject.asObservable();

  private arcanaSubject = new BehaviorSubject<Skill>(
    { name: 'arcana', proficiency: false } //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
  );
  public arcana$ = this.arcanaSubject.asObservable();

  private athleticsSubject = new BehaviorSubject<Skill>(
    { name: 'athletics', proficiency: true } //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
  );
  public athletics$ = this.athleticsSubject.asObservable();

  private deceptionSubject = new BehaviorSubject<Skill>(
    { name: 'deception', proficiency: false } //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
  );
  public deception$ = this.deceptionSubject.asObservable();

  private historySubject = new BehaviorSubject<Skill>(
    { name: 'history', proficiency: false } //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
  );
  public history$ = this.historySubject.asObservable();

  private insightSubject = new BehaviorSubject<Skill>(
    { name: 'insight', proficiency: false } //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
  );
  public insight$ = this.insightSubject.asObservable();

  private intimidationSubject = new BehaviorSubject<Skill>(
    { name: 'intimidation', proficiency: false } //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
  );
  public intimidation$ = this.intimidationSubject.asObservable();

  private investigationSubject = new BehaviorSubject<Skill>(
    { name: 'investigation', proficiency: false } //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
  );
  public investigation$ = this.investigationSubject.asObservable();

  private medicineSubject = new BehaviorSubject<Skill>(
    { name: 'medicine', proficiency: false } //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
  );
  public medicine$ = this.medicineSubject.asObservable();

  private natureSubject = new BehaviorSubject<Skill>(
    { name: 'nature', proficiency: false } //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
  );
  public nature$ = this.natureSubject.asObservable();

  private perceptionSubject = new BehaviorSubject<Skill>(
    { name: 'perception', proficiency: false } //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
  );
  public perception$ = this.perceptionSubject.asObservable();

  private performanceSubject = new BehaviorSubject<Skill>(
    { name: 'performance', proficiency: false } //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
  );
  public performance$ = this.performanceSubject.asObservable();

  private persuasionSubject = new BehaviorSubject<Skill>(
    { name: 'persuasion', proficiency: false } //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
  );
  public persuasion$ = this.persuasionSubject.asObservable();

  private religionSubject = new BehaviorSubject<Skill>(
    { name: 'religion', proficiency: false } //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
  );
  public religion$ = this.religionSubject.asObservable();

  private sleightOfHandSubject = new BehaviorSubject<Skill>(
    { name: 'sleightOfHand', proficiency: false } //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
  );
  public sleightOfHand$ = this.sleightOfHandSubject.asObservable();

  private stealthSubject = new BehaviorSubject<Skill>(
    { name: 'stealth', proficiency: false } //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
  );
  public stealth$ = this.stealthSubject.asObservable();

  private survivalSubject = new BehaviorSubject<Skill>(
    { name: 'survival', proficiency: false } //erstmal nur gemockt, später muss der Wert aus der Datenbank kommen
  );
  public survival$ = this.survivalSubject.asObservable();

  // #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 deleteCollection(collection: string) {
    this.db.collection(collection).delete();
  }

  // #endregion

  // #region character selection and construction

  /**
   *
   * @param character The name of the character that was selected
   */
  public async selectCharacter(character: string) {
    console.log('selectCharacter ausgewählt');
    const characterData = await this.getCollectionWithKeys(character);
    this.character = this.buildCurrentCharacter(characterData);
    console.log('character: ', this.character);
    this.Router.navigate(['journal']);
  }

  private buildCurrentCharacter(currentCharacterData: any): Character {
    const [
      appearanceData,
      attributesData,
      characterData,
      combatStatsData,
      personalityData,
      skillsData,
      weaponsData,
    ] = currentCharacterData.map((entry: any) => entry.data);

    const appearance = {
      age: appearanceData.age,
      height: appearanceData.height,
      weight: appearanceData.weight,
      eyes: appearanceData.eyes,
      skin: appearanceData.skin,
      hair: appearanceData.hair,
    };

    const attributes = {
      strength: attributesData.strength,
      dexterity: attributesData.dexterity,
      constitution: attributesData.constitution,
      intelligence: attributesData.intelligence,
      wisdom: attributesData.wisdom,
      charisma: attributesData.charisma,
    };

    const character = {
      name: characterData.name,
      race: characterData.race,
      class: characterData.class,
      subclass: characterData.subclass,
      level: characterData.level,
      background: characterData.background,
      experience: characterData.experience,
      inspiration: characterData.inspiration,
      proficiencyBonus: characterData.proficiencyBonus,
    };

    const combatStats = {
      armorClass: combatStatsData.armorClass,
      initiative: combatStatsData.initiative,
      speed: combatStatsData.speed,
      hitPointMaximum: combatStatsData.hitPointMaximum,
      currentHitPoints: combatStatsData.currentHitPoints,
      temporaryHitPoints: combatStatsData.temporaryHitPoints,
      hitDice: combatStatsData.hitDice,
      deathSaveSuccesses: combatStatsData.deathSaveSuccesses,
      deathSaveFailures: combatStatsData.deathSaveFailures,
    };

    const personality = {
      personalityTraits: personalityData.personalityTraits,
      ideals: personalityData.ideals,
      bonds: personalityData.bonds,
      flaws: personalityData.flaws,
    };

    const skills = {
      acrobatics: skillsData.acrobatics,
      animalHandling: skillsData.animalHandling,
      arcana: skillsData.arcana,
      athletics: skillsData.athletics,
      deception: skillsData.deception,
      history: skillsData.history,
      insight: skillsData.insight,
      intimidation: skillsData.intimidation,
      investigation: skillsData.investigation,
      medicine: skillsData.medicine,
      nature: skillsData.nature,
      perception: skillsData.perception,
      performance: skillsData.performance,
      persuasion: skillsData.persuasion,
      religion: skillsData.religion,
      sleightOfHand: skillsData.sleightOfHand,
      stealth: skillsData.stealth,
      survival: skillsData.survival,
    };
    const weapons = {
      name: weaponsData.name,
      attackBonus: weaponsData.attackBonus,
      damage: weaponsData.damage,
      damageType: weaponsData.damageType,
      range: weaponsData.range,
      description: weaponsData.damageDice,
    };
    const newCharacter: Character = {
      appearance: appearance,
      attributes: attributes,
      characterData: character,
      combatStats: combatStats,
      personality: personality,
      skills: skills,
      weapons: weapons,
    };
    console.log('newCharacter: ', newCharacter);
    return newCharacter;
  }
  // #endregion
}
