import {Component, inject, Injectable, Input, input, signal} from '@angular/core';
import {CommonModule} from '@angular/common';
import {HttpClient} from '@angular/common/http';
import {QueryEngine} from '@comunica/query-sparql';
import {DataFactory} from 'rdf-data-factory';
import {Store} from 'n3';
import jsonld from 'jsonld';
import {FormsModule} from '@angular/forms';
import {RouterOutlet} from '@angular/router';
import {FahrzeugType, Risikoobjekt, RisikoobjektView} from '../risikoobjektView/risikoobjektView';
import {RisikoobjektService} from '../services/risikoobjekt.service';

interface Produktbaustein {
  id : string;
  bez: string;
  type: string;
  praemienfaktor: string;
  minOcc: number;
  maxOcc: number;
  occByParent: number;
  actualOcc: { value: number };
  showAttribute: boolean;
  parent: Produktbaustein;
  unterbausteine: Produktbaustein[];
  risikoobjekte: (FahrzeugType)[];
  attribute: Attribut[];
  meldungen: Meldung[];
}

interface Meldung {
  id: string
  errorType: number;
  errorMsg: string;
  kommtVonPlausi: string;
}

interface Attribut {
  id: string
  bez: string;
  aenderbar: boolean;
  pflichtfeld: boolean;
  produktId: string;
  value?: string | number | boolean;
  default?: string | boolean | number;
  type: string

}

interface BooleanAttribut extends Attribut{
  default: boolean;
  value: boolean;
}

interface StringAttribut extends Attribut{
  default: string;
  value: string;
}

interface IntAttribut extends Attribut{
  default: number;
  value: number;
  max: number;
  min: number;
}

interface DecimalAttribut extends Attribut{
  default: number;
  value: number;
  max: number;
  min: number;
}

interface Plausi {
  beschreibung: string;
  query: string;
  art: string;
}


@Component({
  selector: 'app-root',
  imports: [CommonModule, FormsModule],
  templateUrl: './produktbaum.html',
  styleUrl: './produktbaum.css'
})
@Injectable({providedIn: 'root'})
export class Produktbaum {
  protected risikoobjektService = inject(RisikoobjektService);
  risikoobjekte = this.risikoobjektService.risikoobjekte;

  protected readonly title = signal('OMDSAngularWebClient');

  http = inject(HttpClient);
  produkte : Produktbaustein[] = [];
  verkaufsprodukte : Produktbaustein[] = [];
  attribute : Attribut[] = [];
  plausis : Plausi[] = [];
  meldungen : Meldung[] = []
  aprioriProdukte : Produktbaustein[] = [];
  apriori(){
    this.http.post<any[]>('produktApi/ProductsRequest', { "stichtag": "2022-02-11" })
      .subscribe(result => {
        this.produkte = [];
        this.attribute = [];
        this.plausis = [];
        this.meldungen = [];

        const prod = 'http://vvo.pisanoapi.at/ProdElement';
        const plaus = 'http://vvo.pisanoapi.at/Plausi';
        const att = 'http://vvo.pisanoapi.at/Elem';
        const meld = 'http://vvo.pisanoapi.at/Meldung';

        result.forEach(p => {
          const id : string = this.extractValue(p['@id']);
          if (id.startsWith(meld)) {
            this.meldungen.push(this.buildMeldung(p))
          }else if (id.startsWith(plaus)) {
            const tmpPlaus = this.buildPlausi(p);
            this.plausis.push(tmpPlaus);
          }else if (id.startsWith(att)){
            this.attribute.push(this.buildAttribut(p))
          }else if (id.startsWith(prod)){
            this.produkte.push(this.buildProdukt(p))
          }

        })

        this.produkte = this.sortProdukte(this.produkte);

        for (const att of this.attribute){
          for (const produkt of this.produkte) {
            if (produkt.id.startsWith(att.produktId)) {
              produkt.attribute.push(att);
            }
          }
        }

        this.buildTree();
        this.verkaufsprodukte = this.produkte.filter(p => p.parent === undefined);
        this.aprioriProdukte = structuredClone(this.produkte);
      });

  }

  buildAttribut(p: any) : (DecimalAttribut | IntAttribut | BooleanAttribut | StringAttribut){
    const keyMapping: { [key: string]: string } = {
      '@id': 'id',
      'http://vvo.pisanoapi.at/bez': 'bez',
      'http://vvo.pisanoapi.at/required': 'required',
      'http://vvo.pisanoapi.at/value': 'value',
      'http://vvo.pisanoapi.at/max': 'max',
      'http://vvo.pisanoapi.at/min': 'min',
      'http://vvo.pisanoapi.at/default': 'default',
      'http://vvo.pisanoapi.at/aenderbar': 'aenderbar',
      'http://vvo.pisanoapi.at/ProdElement': 'produktId',
    };

    const attribut: any = {};

    for (const key in p) {
      const mappedKey = keyMapping[key];

      if (mappedKey){
        let value =  this.extractValue(p[key]);

        if (mappedKey === 'id' && typeof value === 'string') {
          if (value.startsWith("vvo:")){
            value = value.substring(4)
          }else {
            value = value.substring(24);
          }
          attribut.type = value.substring(4)
        }

        if (mappedKey === 'produktId' && typeof value === 'string') {
          if (value.startsWith("vvo:")){
            value = value.substring(15);
          }else {
            value = value.substring(35);
          }
        }

        if (mappedKey === 'value') {
          value = this.parseDynamicValue(value);
        }

        if (mappedKey === 'default') {
          value = this.parseDynamicValue(value)
          if (typeof value === "boolean" && value) {
            attribut.value = true
          }
        }

        if (mappedKey === 'aenderbar') {
          value = this.parseDynamicValue(value)
        }


        attribut[mappedKey] = value;
      }
    }

    return attribut as (DecimalAttribut | IntAttribut | BooleanAttribut | StringAttribut);
  }

  buildMeldung(p: any) : Meldung{
    const keyMapping: { [key: string]: string } = {
      '@id': 'id',
      'http://vvo.pisanoapi.at/errorMsg': 'errorMsg',
      'http://vvo.pisanoapi.at/errorType': 'errorType',
      'vvo:errorMsg': 'errorMsg',
      'vvo:errorType': 'errorType'
    };

    const meldung: any = {};

    for (const key in p) {
      const mappedKey = keyMapping[key];

      if (mappedKey){
        let value =  this.extractValue(p[key]);

        if (mappedKey === 'id' && typeof value === 'string') {
          if (value.startsWith("vvo:")){
            value = value.substring(4);
          }else {
            value = value.substring(24);
          }
        }


        meldung[mappedKey] = value;
      }
    }

    return meldung as Meldung;
  }

  parseDynamicValue(value: string): string | boolean | number {
    if (value.toLowerCase() === 'true') return true;
    if (value.toLowerCase() === 'false') return false;

    if (!isNaN(Number(value)) && value.trim() !== '') {
      const num = Number(value);
      return Number.isInteger(num) ? num : parseFloat(value);
    }

    return value;
  }

  extractValue(data: any): any {
    if (!data) return undefined;

    const container = Array.isArray(data) ? data[0] : data;

    if (container && typeof container === 'object' && container['@value'] !== undefined) {
      return container['@value'].trim();
    }else if (container && typeof container === 'object' && container['@id'] !== undefined) {
      return container['@id'].trim();
    }

    return container;
  }

  buildPlausi(p: any): Plausi {
    const keyMapping: { [key: string]: string } = {
      '@id': 'id',
      'http://vvo.pisanoapi.at/beschreibung': 'beschreibung',
      'http://vvo.pisanoapi.at/query': 'query',
      'http://vvo.pisanoapi.at/art': 'art',
      'vvo:beschreibung': 'beschreibung',
      'vvo:query': 'query',
      'vvo:art': 'art'
    };

    const plausi: any = {};

    for (const key in p) {
      const mappedKey = keyMapping[key];

      if (mappedKey){
        let value = this.extractValue(p[key]);

        plausi[mappedKey] = value;
      }
    }

    return plausi as Plausi;
  }

  buildFahrzeug(p: any): FahrzeugType {
    const keyMapping: { [key: string]: string } = {
      '@id': 'id',
      'http://vvo.pisanoapi.at/bez': 'handelsbezeichnung',
      'http://vvo.pisanoapi.at/baujahr': 'baujahr',
      'http://vvo.pisanoapi.at/erstzulassung': 'erstzulassung',
      'http://vvo.pisanoapi.at/leistung': 'leistung',
      'http://vvo.pisanoapi.at/listenpreis': 'listenpreis',
      'http://vvo.pisanoapi.at/sonderausstattung': 'sonderausstattung',
      'http://vvo.pisanoapi.at/kennzeichen': 'kennzeichen'

    };

    const fahrzeug: any = {};

    for (const key in p) {
      const mappedKey = keyMapping[key];

      if (mappedKey){
        let value = this.extractValue(p[key]);


        if (mappedKey === 'id' && typeof value === 'string') {
          value = value.substring(24);
        }

        if (mappedKey === 'baujahr' && typeof value === 'string') {
          value = parseInt(value, 10);
          if (isNaN(value)) value = undefined;
        }

        if (mappedKey === 'erstzulassung' && typeof value === 'string') {
          value = new Date(value.substring(0, 10));
        }

        fahrzeug[mappedKey] = value;
      }
    }

    return fahrzeug as FahrzeugType;
  }

  buildProdukt(p: any): Produktbaustein {
    const keyMapping: { [key: string]: string } = {
      '@id': 'id',
      'id': 'id',
      'http://vvo.pisanoapi.at/bez': 'bez',
      'http://vvo.pisanoapi.at/minOccurrence': 'minOcc',
      'http://vvo.pisanoapi.at/maxOccurrence': 'maxOcc',
      'http://vvo.pisanoapi.at/type': 'type',
      'http://vvo.pisanoapi.at/Parent': 'parent',
      'http://vvo.pisanoapi.at/Meldung': 'meldungen',
      'http://vvo.pisanoapi.at/VersichertesInteresseType' : 'risikoobjekte',
      'http://vvo.pisanoapi.at/praemienfaktor' : 'praemienfaktor',

    };

    const produkt: any = {};
    produkt.meldungen = [];
    produkt.risikoobjekte = [];

    for (const key in p) {
      const mappedKey = keyMapping[key];

      if (mappedKey){
        let value = this.extractValue(p[key]);

        if (mappedKey === 'risikoobjekte') {
          const values = Array.isArray(p[key]) ? p[key] : [p[key]];

          for (const v of values) {
            const value = this.extractValue(v);

            const ro = this.risikoobjektService.risikoobjekte().find(
              r => r.id === value.substring(24)
            );

            if (ro) {
              produkt.risikoobjekte.push(ro);
            } else {
              console.log("Risiko Objekt nicht gefunden: " + value);
            }
          }
        }

        if ((mappedKey === 'minOcc' || mappedKey === 'maxOcc') && typeof value === 'string') {
          value = parseInt(value, 10);
          if (isNaN(value)) value = undefined;
        }

        if (mappedKey === 'id' && typeof value === 'string') {
          // if(value.includes("-")) {
          //   value = value.substring(0, value.indexOf("-"))
          // }
          if (value.startsWith("vvo:")) {
            value = value.substring(15)
          }else {
            value = value.substring(35);
          }
        }

        if (mappedKey === 'parent' && typeof value === 'string') {
          if (value.startsWith("vvo:")){
            value = this.produkte.find(p => p.id === value.substring(15));
          }else {
            value = this.produkte.find(p => p.id === value.substring(35));
          }
        }

        if (mappedKey === 'meldungen' && typeof value === 'string') {
          if (value.startsWith("vvo:")){
            value = this.meldungen.find(p => p.id === value.substring(4));
          }else {
            value = this.meldungen.find(p => p.id === value.substring(24));
          }

          if (value !== undefined){
            produkt[mappedKey].push(value)
          }
        }

        if (mappedKey !== "meldungen" && mappedKey !== "risikoobjekte") {
          produkt[mappedKey] = value;
        }
      }
    }
    produkt.unterbausteine = [];
    produkt.attribute = [];

    produkt.occByParent = (produkt.parent === undefined || produkt.minOcc > 0) ? 1 : 0;
    produkt.actualOcc = { value: produkt.occByParent };

    return produkt as Produktbaustein;
  }

  buildTree() {


    for (const baustein of this.produkte) {
      if (baustein.parent !== undefined){
        let parent : (Produktbaustein | undefined) = this.produkte.find(p => p === baustein.parent);
        if (parent !== undefined && !parent.unterbausteine.includes(baustein)) {
          if (parent.occByParent < 1) {
            baustein.occByParent = 0
          }
          parent.unterbausteine.push(baustein);
        }
      }
    }
    this.addMissingAprioriItems();
  }

  async addProdukt(produkt : Produktbaustein) {
    for (const unterprodukt of produkt.parent.unterbausteine){
      if (unterprodukt.id === produkt.id){
        unterprodukt.occByParent++;
      }
    }
    if (produkt.occByParent >= 2) {
      const parent = this.produkte.find(p => p === produkt.parent)
      parent?.unterbausteine.push(this.cloneProdukt(produkt, undefined));

    }else {
      produkt.actualOcc.value++;
    }

    this.addRequiredChildren(produkt)
    await this.constructPlausi()
  }

  addParent(produkt : Produktbaustein) {
    if (produkt.parent.occByParent < 1) {
      produkt.parent.occByParent = 1

      this.addParent(produkt.parent)
    }
  }

  addRequiredChildren(produkt : Produktbaustein) {
    for (const unter of produkt.unterbausteine) {
      if (unter.minOcc > 0 && unter.occByParent < 1) {
        unter.occByParent = 1
        if (unter.unterbausteine.length > 0) {
          this.addRequiredChildren(unter)
        }
      }
    }
  }

  async constructPlausi() {


    for (const plausi of this.plausis.filter(p => p.art === "graph")) {

      for (const prod of this.produkte) {
        for (const meld of prod.meldungen) {
          if (meld.kommtVonPlausi === plausi.beschreibung) {
            prod.meldungen.splice(prod.meldungen.indexOf(meld), 1);
          }
        }
      }


      let model = await this.buildRDFModel(this.verkaufsprodukte[0]);
      model = await this.pruefePlausi(plausi, model);
      const newTree = await this.modelToJsonld(model);

      // this.produkte = [];
      // this.attribute = [];
      // this.meldungen = [];

      const prod = 'http://vvo.pisanoapi.at/ProdElement';
      const plaus = 'vvo:Plausi';
      const att = 'vvo:Elem';
      const meld = 'http://vvo.pisanoapi.at/Meldung';

      let uniqueIds: any[] = [];
      let wholeprodukts: any[] = []

      newTree.forEach((p: any) => {if(p !== undefined) p.forEach((q: any) => {
        if (!uniqueIds.includes(q["@id"])) {
          uniqueIds.push(q["@id"]);
        }
      })});

      for (let id of uniqueIds) {
        let produkt: any[] = [];

        newTree.forEach((p: any) => p.filter((p: any) => p["@id"] === id).forEach((i: any) => {
          produkt.push(i)
        }));
        wholeprodukts.push(produkt);

      }

      for (const q of wholeprodukts) {
        const merged = Object.assign({}, ...q);

        if (q[0]["@id"].startsWith(meld)) {
          this.meldungen.push(this.buildMeldung(merged))
        } else if (q[0]["@id"].startsWith(plaus)) {
          const tmpPlaus = this.buildPlausi(merged);
          this.plausis.push(tmpPlaus);
        } else if (q[0]["@id"].startsWith(att)) {
          this.attribute.push(this.buildAttribut(merged))
        }
      }

      for (const q of wholeprodukts) {
        const merged = Object.assign({}, ...q);

        if (q[0]["@id"].includes(prod)) {
          let newProd = this.buildProdukt(merged);
          if (newProd.bez === undefined) {
            const prod = this.produkte.find(p => newProd.id.includes(p.id));
            if (prod !== undefined) {
              //für andere sachen als meldung genauso, wenn wir Plausis wollen, die mehr machen als Meldungen hinzufügen
              for (const newMeldung of newProd.meldungen) {
                newMeldung.kommtVonPlausi = plausi.beschreibung
                prod.meldungen.push(newMeldung)
              }
            }
          }
        }
      }

      for (const att of this.attribute) {
        for (const produkt of this.produkte) {
          if (produkt.id.startsWith(att.produktId) && !produkt.attribute.includes(att)) {
            produkt.attribute.push(att);
          }
        }
      }

      this.buildTree();
      this.verkaufsprodukte = this.produkte.filter(p => p.parent === undefined);
    }
  }

  cloneProdukt(produkt : Produktbaustein, newParent: (Produktbaustein | undefined)) : Produktbaustein {
    const cloneProdukt = structuredClone(produkt);

    if (newParent === undefined) {
      cloneProdukt.parent = produkt.parent
    }else {
      cloneProdukt.parent = newParent;
    }
    cloneProdukt.unterbausteine = []
    cloneProdukt.actualOcc = produkt.actualOcc;
    cloneProdukt.actualOcc.value++;

    for (const child of produkt.unterbausteine) {
      cloneProdukt.unterbausteine.push(this.cloneProdukt(child, cloneProdukt))
    }

    this.produkte.push(cloneProdukt);
    return cloneProdukt;
  }

  async removeProdukt(produkt : Produktbaustein) {
    if (produkt.occByParent >= 2){
      const parent = this.produkte.find(p => p === produkt.parent)
      if (parent !== undefined){
        const i = parent?.unterbausteine.indexOf(produkt);
        const j = this.produkte.indexOf(produkt);
        if (i > -1) {
          parent.unterbausteine.splice(i, 1);
          this.produkte.splice(j, 1);
        }
      }
    }
    produkt.actualOcc.value--;

    for (const unterprodukt of produkt.parent.unterbausteine){
      if (unterprodukt.id === produkt.id){
        unterprodukt.occByParent--;
      }
    }

    if (produkt.unterbausteine.length > 0) {
      this.removeChildren(produkt);
    }

    await this.constructPlausi()
  }

  removeChildren(produkt : Produktbaustein) {
    const tmp : string[] = [];
    const toDelete : Produktbaustein[] = [];

    for (const child of produkt.unterbausteine){
      if (!tmp.includes(child.id)){
        child.actualOcc.value = child.actualOcc.value - child.occByParent;
        tmp.push(child.id);
      }

      if (child.occByParent > 1){
        toDelete.push(child)

        for (const unterbaustein of produkt.unterbausteine){
          unterbaustein.occByParent--;
        }
      }
      child.occByParent = 0;

      this.removeChildren(child);
    }

    for (const e of toDelete){
      produkt.unterbausteine.splice(produkt.unterbausteine.indexOf(e) ,1)
    }
  }

  async calculate(verkaufsprodukt: Produktbaustein) {
    const request = (await this.modelToJsonld(await this.buildRDFModel(verkaufsprodukt)));
    this.produkte = [];
    this.http.post<any[]>('produktApi/CalculateRequest', request )
      .subscribe(result => {
        this.produkte = [];
        this.attribute = [];

        const prod = 'http://vvo.pisanoapi.at/ProdElement';
        const plaus = 'http://vvo.pisanoapi.at/Plausi';
        const att = 'http://vvo.pisanoapi.at/Elem';
        const meld = 'http://vvo.pisanoapi.at/Meldung';
        const fz = 'http://vvo.pisanoapi.at/FahrzeugType';

        result.forEach(p => {
          const id : string = this.extractValue(p['@id']);
          if (id.startsWith(meld)) {
            this.meldungen.push(this.buildMeldung(p))
          }else if (id.startsWith(plaus)) {
            const tmpPlaus = this.buildPlausi(p);
            this.plausis.push(tmpPlaus);
          }else if (id.startsWith(att)){
            this.attribute.push(this.buildAttribut(p))
          }else if (id.startsWith(fz)){
            const fahrzeug = this.buildFahrzeug(p);

            let fahrzeugAlt =
              this.risikoobjektService.risikoobjekte().find(r =>
                r.handelsbezeichnung === fahrzeug.handelsbezeichnung
                && new Date(r.erstzulassung).getTime() === fahrzeug.erstzulassung.getTime())

            if (fahrzeugAlt !== undefined){
              //alle attribute die sich geädert haben könnten
              fahrzeugAlt.id = fahrzeug.id;
              fahrzeugAlt.baujahr = fahrzeug.baujahr;
            } else {
              console.log("neues fahrzeug")
              this.risikoobjektService.addRisikoobjekt(fahrzeug);
            }
          }
        })

        result.forEach(p => {
          const id : string = this.extractValue(p['@id']);

          if (id.startsWith(prod)){
            this.produkte.push(this.buildProdukt(p))
          }
        })

        this.produkte = this.sortProdukte(this.produkte);

        for (const att of this.attribute){
          for (const produkt of this.produkte) {
            if (produkt.id.startsWith(att.produktId)) {
              produkt.attribute.push(att);
            }
          }
        }

        this.buildTree();
        this.verkaufsprodukte = this.produkte.filter(p => p.parent === undefined);
      });
  }

  sortProdukte(produkte: Produktbaustein[]) : Produktbaustein[] {
    produkte.sort((a, b) => {
      return a.bez < b.bez ? -1 : a.bez > b.bez ? 1 : 0
    });

    return produkte;
  }

  async buildRDFModel(verkaufsprodukt : Produktbaustein) {
    const model = new Store();
    const baseIri = 'http://vvo.pisanoapi.at/';

    const idCount = new Map<string, number>();
    this.createIdCount(idCount, verkaufsprodukt);

    const iriMap = new Map<Produktbaustein, string>();
    this.createIriMap(verkaufsprodukt, baseIri, idCount, iriMap);

    this.addProduktToModel(verkaufsprodukt, model, baseIri, null, iriMap);

    return model
  }

  async modelToJsonld(model: Store) : Promise<any[]> {
    let result = []
    const jsonldData = model.getQuads(null, null, null, null).map(q => ({
      '@id': q.subject.value,
      [q.predicate.value]: [
        q.object.termType === 'Literal'
          ? { '@value': q.object.value }
          : { '@id': q.object.value }
      ]
    }));

    const context = { vvo: 'http://vvo.pisanoapi.at/' };
    result.push(await jsonld.expand(jsonldData));

    return result;
  }

  createIdCount(idCount: Map<string, number>, vp: Produktbaustein): void {
    const id = vp.id;
    idCount.set(id, (idCount.get(id) ?? 0) + 1);
    for (const child of vp.unterbausteine) {
      this.createIdCount(idCount, child);
    }
  }

  createIriMap(
    produkt: Produktbaustein,
    baseIri: string,
    idCount: Map<string, number>,
    iriMap: Map<Produktbaustein, string>
  ): void {
    const baseElemIri = baseIri + 'ProdElement';
    const id = produkt.id;
    const count = idCount.get(id) ?? 1;

    for (let i = 1; i <= count; i++) {
      let tmpIri = `${baseElemIri}${id}-${i}`;
      if (id.includes("-")){
        tmpIri = `${baseElemIri}${id}`;
      }
      if (![...iriMap.values()].includes(tmpIri)) {
        iriMap.set(produkt, tmpIri);
        break;
      }
    }

    for (const child of produkt.unterbausteine) {
      this.createIriMap(child, baseIri, idCount, iriMap);
    }
  }

  addProduktToModel(
    produkt: Produktbaustein,
    model: Store,
    baseIri: string,
    parentId: string | null,
    iriMap: Map<Produktbaustein, string>
  ) {
    const DF = new DataFactory()
    const vpIri = DF.namedNode(iriMap.get(produkt)!);

    if (produkt.occByParent >= 1) {
      model.addQuad(vpIri, DF.namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), DF.namedNode("http://vvo.pisanoapi.at/ProdElement"))
      model.addQuad(vpIri, DF.namedNode(baseIri + 'bez'), DF.literal(produkt.bez));

      if (produkt.minOcc !== undefined)
        model.addQuad(vpIri, DF.namedNode(baseIri + 'minOccurrence'), DF.literal(produkt.minOcc.toString()));
      if (produkt.maxOcc !== undefined)
        model.addQuad(vpIri, DF.namedNode(baseIri + 'maxOccurrence'), DF.literal(produkt.maxOcc.toString()));

      // if (produkt.verkaufsoffenVon)
      //   model.addQuad(vpIri, DF.namedNode(baseIri + 'salesFrom'), DF.literal(produkt.verkaufsoffenVon));
      // if (produkt.verkaufsoffenBis)
      //   model.addQuad(vpIri, DF.namedNode(baseIri + 'salesTo'), DF.literal(produkt.verkaufsoffenBis));
      if (produkt.type)
        model.addQuad(vpIri, DF.namedNode(baseIri + 'type'), DF.literal(produkt.type));
      // if (produkt.risikoobjektErforderlich)
      //   model.addQuad(vpIri, DF.namedNode(baseIri + 'risikoobjektType'), DF.literal('FahrzeugType'));
      if (produkt.praemienfaktor)
        model.addQuad(vpIri, DF.namedNode(baseIri + 'praemienfaktor'), DF.literal(produkt.praemienfaktor));
      if (parentId)
        model.addQuad(vpIri, DF.namedNode(baseIri + 'Parent'), DF.namedNode(parentId));
    }

    for (const at of produkt.attribute ?? []) {
      let typeIri = baseIri;
      if (at.type.toLowerCase().includes("decimal")){
        typeIri += "ElemDecimal";
      }else if (at.type.toLowerCase().includes("int")){
        typeIri += "ElemInt";
      }else if (at.type.toLowerCase().includes("bool")){
        typeIri += "ElemBoolean";
      }else {
        typeIri += "ElemString";
      }

      const atIri = DF.namedNode(`${baseIri}${at.id}`);
      model.addQuad(atIri, DF.namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), DF.namedNode(typeIri));

      model.addQuad(atIri, DF.namedNode(baseIri + 'bez'), DF.literal(at.bez));
      model.addQuad(atIri, DF.namedNode(baseIri + 'ProdElement'), vpIri);

      if (typeof at.value === "string") {
        model.addQuad(atIri, DF.namedNode(baseIri + 'value'), DF.literal(at.value));
      }else if (typeof at.value === "boolean"){
        model.addQuad(atIri, DF.namedNode(baseIri + 'value'), DF.literal(at.value ? "true" : "false"));
      } else if (at.value !== undefined) {
        model.addQuad(atIri, DF.namedNode(baseIri + 'value'), DF.literal(at.value.toString()));
      }
    }

    let i = 1;
    for (const ro of produkt.risikoobjekte) {
      const fahrzeugIri = DF.namedNode(baseIri + "FahrzeugType")
      const spezFahrzeugIri = DF.namedNode(fahrzeugIri.value + ro.handelsbezeichnung + ro.baujahr);

      model.addQuad(vpIri, DF.namedNode(baseIri + "VersichertesInteresseType"), spezFahrzeugIri);

      const existingQuads = model.getQuads(spezFahrzeugIri, null, null, null);
      if (existingQuads.length === 0) {
        model.addQuad(spezFahrzeugIri, DF.namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), fahrzeugIri);

        if (ro.handelsbezeichnung) {
          model.addQuad(spezFahrzeugIri, DF.namedNode(baseIri + 'bez'), DF.literal(ro.handelsbezeichnung));
        }
        if (ro.baujahr !== undefined) {
          model.addQuad(spezFahrzeugIri, DF.namedNode(baseIri + 'baujahr'), DF.literal(ro.baujahr.toString()));
        }
        if (ro.erstzulassung) {
          model.addQuad(spezFahrzeugIri, DF.namedNode(baseIri + 'erstzulassung'), DF.literal( ro.erstzulassung.toString()));
        }
        if (ro.kennzeichen) {
          model.addQuad(spezFahrzeugIri, DF.namedNode(baseIri + 'kennzeichen'), DF.literal( ro.kennzeichen));
        }
        if (ro.leistung) {
          model.addQuad(spezFahrzeugIri, DF.namedNode(baseIri + 'leistung'), DF.literal( ro.leistung.toString()));
        }
        if (ro.listenpreis) {
          model.addQuad(spezFahrzeugIri, DF.namedNode(baseIri + 'listenpreis'), DF.literal( ro.listenpreis.toString()));
        }
        if (ro.sonderausstattung) {
          model.addQuad(spezFahrzeugIri, DF.namedNode(baseIri + 'sonderausstattung'), DF.literal( ro.sonderausstattung.toString()));
        }
      }

      i++;
    }

    for (const child of produkt.unterbausteine) {
      const unterIri = iriMap.get(child)!;
      if (produkt.occByParent >= 1) {
        model.addQuad(vpIri, DF.namedNode(baseIri + 'Baustein'), DF.namedNode(unterIri));
      }
      this.addProduktToModel(child, model, baseIri, vpIri.value, iriMap);
    }
  }

  async pruefePlausi(plausi: Plausi, model: Store): Promise<Store> {
    const engine = new QueryEngine();
    if (plausi.art === "graph"){
      const result = await engine.queryQuads(plausi.query, { sources: [model],});
      const quads = await result.toArray();

      const erg : Store = new Store();
      for (const quad of quads) {
        erg.addQuad(quad.subject, quad.predicate, quad.object);
      }
      return erg;
    }
    return model;
  }

  toggleAttributes(baustein: Produktbaustein) {
    baustein.showAttribute = !baustein.showAttribute;
  }

  getInputType(attr: Attribut): string {
    const a = attr as BooleanAttribut | StringAttribut | IntAttribut | DecimalAttribut;

    if (typeof a.value === 'boolean' || typeof a.default === 'boolean') return 'checkbox';
    if (typeof a.value === 'number' || typeof a.default === 'number') return 'number';
    return 'text';
  }

  attributValueListener(attr: Attribut) {
    console.log(attr.value)
  }
  setBooleanAttributValue(event: Event, attr: Attribut) {
    attr.value = (event.target as HTMLInputElement).checked
  }

  addMissingAprioriItems(){
    for (const aprod of this.aprioriProdukte) {
      let flag = false;

      for (const prod of this.produkte) {
        if (prod.bez === aprod.bez) {
          flag = true
        }
      }

      if (!flag) {
        aprod.occByParent = 0
        this.removeChildren(aprod)
        const newParent = this.produkte.find(p => p.bez === aprod.parent.bez)
        if (newParent !== undefined) {
          aprod.parent = newParent
        }
        this.produkte.push(this.cloneProdukt(aprod, undefined))
      }
    }
  }

  risikoobjektListener(baustein: Produktbaustein, ro: FahrzeugType, event: Event) {
    if ((event.target as HTMLInputElement).checked) {
      baustein.risikoobjekte.push(ro)
    } else {
      baustein.risikoobjekte.splice(baustein.risikoobjekte.indexOf(ro), 1)
    }
  }
}
