package at.vvo.omds.helpers;

import at.vvo.omds.types.omds3.r2025_05.common.*;
import at.vvo.omds.types.omds3.r2025_05.on2antrag.sachPrivat.RisikoGebaeudeType;
import at.vvo.omds.types.omds3.r2025_05.on2antrag.sachPrivat.RisikoHaushaltType;
import at.vvo.omds.types.omds3.r2025_05.on2antrag.sachPrivat.VersichertesObjektSachPrivatType;
import jakarta.xml.bind.JAXBElement;
import javafx.scene.control.TreeItem;
import javafx.util.Pair;
import org.eclipse.rdf4j.model.*;
import org.eclipse.rdf4j.model.impl.LinkedHashModel;
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
import org.eclipse.rdf4j.model.util.Models;
import org.eclipse.rdf4j.model.util.Values;
import org.eclipse.rdf4j.model.vocabulary.RDF;
import org.eclipse.rdf4j.rio.RDFFormat;
import org.eclipse.rdf4j.rio.Rio;
import org.springframework.http.MediaType;
import org.springframework.web.client.RestClient;

import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import java.io.IOException;
import java.io.StringReader;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;

import static org.eclipse.rdf4j.model.util.Values.iri;

public class RDFHelper {

    private final RestClient restClient = RestClient.create();

    public Pair<List<APrioriVerkaufsproduktType>, List<Plausi>> getAprioriRDF(JAXBElement<ProductsRequest> r) throws DatatypeConfigurationException, IOException {
        String response = restClient.post()
                .uri("http://localhost:9090/ProductsRequest")
                .contentType(MediaType.APPLICATION_JSON)
                .body(r.getValue())
                .retrieve()
                .toEntity(String.class)
                .getBody();


        List<Pair<String, AttributType>> attributeListe = getAttributesFromRequest(response);
        List<APrioriVerkaufsproduktType> result = productsRequestToVerkaufsprodukt(response, attributeListe);
        List<Plausi> result2 = productsRequestToPlausi(response);

        return new Pair<>(result, result2);
    }

    private List<Pair<String, AttributType>> getAttributesFromRequest(String response) throws IOException {
        Model model = Rio.parse(new StringReader(response), "", RDFFormat.JSONLD);
        List<Pair<String, AttributType>> erg = new ArrayList<>();

        String baseIri = "http://vvo.pisanoapi.at/";

        IRI bezIri = iri(baseIri + "bez");
        IRI produktIri = iri(baseIri + "ProdElement");
        IRI requiredIri = iri(baseIri + "required");
        IRI maxIri = iri(baseIri + "max");
        IRI minIri = iri(baseIri + "min");
        IRI defaultIri = iri(baseIri + "default");
        IRI valueIri = iri(baseIri + "value");
        IRI aenderbarIri = iri(baseIri + "aenderbar");


        for (Resource iri : Models.subjectIRIs(model)) {
            String bez = Models.objectLiteral(model.filter(iri, bezIri, null)).map(Literal::getLabel).orElse(null);
            String prodId = Models.objectResource(model.filter(iri, produktIri, null)).map(Resource::stringValue)
                    .orElse(Models.objectLiteral(model.filter(iri, produktIri, null)).map(Literal::getLabel).orElse(null));
            Boolean required = Boolean.valueOf(Models.objectLiteral(model.filter(iri, requiredIri, null)).map(Literal::getLabel).orElse(null));
            Boolean aenderbar = Boolean.valueOf(Models.objectLiteral(model.filter(iri, aenderbarIri, null)).map(Literal::getLabel).orElse("true"));

            if (iri.stringValue().contains("ElemBoolean")) {
                AttributBooleanType ab = new AttributBooleanType();

                Boolean val = Boolean.valueOf(Models.objectLiteral(model.filter(iri, valueIri, null)).map(Literal::getLabel).orElse(null));
                Boolean def = Boolean.valueOf(Models.objectLiteral(model.filter(iri, defaultIri, null)).map(Literal::getLabel).orElse(null));

                int start = iri.stringValue().lastIndexOf("ElemBoolean");
                ab.setId(BigInteger.valueOf(Long.valueOf(iri.stringValue().substring(start + "ElemBoolean".length()))));

                ab.setBezeichnung(bez);
                ab.setDefault(def);
                ab.setPflichtfeld(required);
                ab.setValue(val);
                ab.setAenderbar(aenderbar);

                Pair<String, AttributType> atPair = new Pair<>(prodId, ab);
                erg.add(atPair);
            } else if (iri.stringValue().contains("ElemInt")) {
                AttributIntType ai = new AttributIntType();

                Integer val = Integer.parseInt(Models.objectLiteral(model.filter(iri, valueIri, null)).map(Literal::getLabel).orElse("-1"));
                Integer def = Integer.parseInt(Models.objectLiteral(model.filter(iri, defaultIri, null)).map(Literal::getLabel).orElse("-1"));
                Integer min = Integer.parseInt(Models.objectLiteral(model.filter(iri, minIri, null)).map(Literal::getLabel).orElse("-1"));
                Integer max = Integer.parseInt(Models.objectLiteral(model.filter(iri, maxIri, null)).map(Literal::getLabel).orElse("-1"));

                int start = iri.stringValue().lastIndexOf("ElemInt");
                ai.setId(BigInteger.valueOf(Long.parseLong(iri.stringValue().substring(start + "ElemInt".length()))));

                ai.setBezeichnung(bez);
                ai.setDefault(def);
                ai.setPflichtfeld(required);
                ai.setMax(max);
                ai.setMin(min);
                if (!val.equals(-1))ai.setValue(val);
                ai.setAenderbar(aenderbar);

                Pair<String, AttributType> atPair = new Pair<>(prodId, ai);
                erg.add(atPair);
            } else if (iri.stringValue().contains("ElemDecimal")) {
                AttributDezimalType ad = new AttributDezimalType();

                BigDecimal val = Models.objectLiteral(model.filter(iri, valueIri, null)).map(Literal::decimalValue).orElse(BigDecimal.valueOf(-1));
                BigDecimal def = Models.objectLiteral(model.filter(iri, defaultIri, null)).map(Literal::decimalValue).orElse(BigDecimal.valueOf(-1));
                BigDecimal min = Models.objectLiteral(model.filter(iri, minIri, null)).map(Literal::decimalValue).orElse(BigDecimal.valueOf(-1));
                BigDecimal max = Models.objectLiteral(model.filter(iri, maxIri, null)).map(Literal::decimalValue).orElse(BigDecimal.valueOf(-1));

                int start = iri.stringValue().lastIndexOf("ElemDecimal");
                ad.setId(BigInteger.valueOf(Long.valueOf(iri.stringValue().substring(start + "ElemDecimal".length()))));

                ad.setBezeichnung(bez);
                ad.setDefault(def);
                ad.setPflichtfeld(required);
                ad.setMax(max);
                ad.setMin(min);
                if (!val.equals(BigDecimal.valueOf(-1)))ad.setValue(val);
                ad.setAenderbar(aenderbar);

                Pair<String, AttributType> atPair = new Pair<>(prodId, ad);
                erg.add(atPair);
            } else if (iri.stringValue().contains("ElemString")) {
                AttributStringType as = new AttributStringType();

                String val = String.valueOf(Models.objectLiteral(model.filter(iri, valueIri, null)).map(Literal::getLabel).orElse(null));
                String def = String.valueOf(Models.objectLiteral(model.filter(iri, defaultIri, null)).map(Literal::getLabel).orElse(null));

                int start = iri.stringValue().lastIndexOf("ElemString");
                as.setId(BigInteger.valueOf(Long.parseLong(iri.stringValue().substring(start + "ElemString".length()))));

                as.setBezeichnung(bez);
                as.setDefault(def);
                as.setPflichtfeld(required);
                as.setValue(val);
                as.setAenderbar(aenderbar);

                Pair<String, AttributType> atPair = new Pair<>(prodId, as);
                erg.add(atPair);
            }
        }

        return erg;
    }

    private List<Plausi> productsRequestToPlausi(String response) throws IOException {
        List<Plausi> result = new ArrayList<>();
        Model model = Rio.parse(new StringReader(response), "", RDFFormat.JSONLD);

        for (Resource iri : Models.subjectIRIs(model)) {
            if (iri.stringValue().contains("Plausi")) {
                String id = iri.stringValue().replace("http://vvo.pisanoapi.at/Plausi", "");
                ValueFactory vf = SimpleValueFactory.getInstance();

                String beschreibung = Models.objectLiteral(model.filter(iri, vf.createIRI(
                        "http://vvo.pisanoapi.at/beschreibung"), null)).map(Literal::getLabel).orElse(null);

                String query = Models.objectLiteral(model.filter(iri, vf.createIRI(
                        "http://vvo.pisanoapi.at/query"), null)).map(Literal::getLabel).orElse(null);

                String art = Models.objectLiteral(model.filter(iri, vf.createIRI(
                        "http://vvo.pisanoapi.at/art"), null)).map(Literal::getLabel).orElse(null);

                result.add(new Plausi(id, beschreibung, art, query));
            }
        }
        return result;
    }

    private List<APrioriVerkaufsproduktType> productsRequestToVerkaufsprodukt(String productsRequest, List<Pair<String, AttributType>> attributeListe) throws IOException, DatatypeConfigurationException {
        Model model = Rio.parse(new StringReader(productsRequest), "", RDFFormat.JSONLD);

        Map<String, APrioriProduktbausteinType> bausteine = new HashMap<>();
        Map<String, String> parents = new HashMap<>();

        for (Resource iri : Models.subjectIRIs(model)) {
            if (iri.stringValue().contains("ProdElement")) {

                String id = iri.stringValue();
                ValueFactory vf = SimpleValueFactory.getInstance();

                int insId = Integer.parseInt(Models.objectLiteral(model.filter(iri, vf.createIRI(
                        "http://vvo.pisanoapi.at/ins_id"), null)).map(Literal::getLabel).orElse("-1"));
                int maxOccurrence = Integer.parseInt(Models.objectLiteral(model.filter(iri, vf.createIRI(
                        "http://vvo.pisanoapi.at/maxOccurrence"), null)).map(Literal::getLabel).orElse("-1"));
                int minOccurrence = Integer.parseInt(Models.objectLiteral(model.filter(iri, vf.createIRI(
                        "http://vvo.pisanoapi.at/minOccurrence"), null)).map(Literal::getLabel).orElse("-1"));
                String bez = Models.objectLiteral(model.filter(iri, vf.createIRI(
                        "http://vvo.pisanoapi.at/bez"), null)).map(Literal::getLabel).orElse(null);
                String type = Models.objectLiteral(model.filter(iri, vf.createIRI(
                        "http://vvo.pisanoapi.at/type"), null)).map(Literal::getLabel).orElse(null);
                String risikoobjektType = Models.objectLiteral(model.filter(iri, vf.createIRI(
                        "http://vvo.pisanoapi.at/risikoobjektType"), null)).map(Literal::getLabel).orElse(null);
                String praemie = Models.objectLiteral(model.filter(iri, vf.createIRI(
                        "http://vvo.pisanoapi.at/praemienfaktor"), null)).map(Literal::getLabel).orElse(null);
                XMLGregorianCalendar verkaufsoffenVon = Models.objectLiteral(model.filter(iri, vf.createIRI(
                        "http://vvo.pisanoapi.at/salesFrom"), null)).map(Literal::getLabel).isPresent() ?
                        DatatypeFactory.newInstance().newXMLGregorianCalendar( Models.objectLiteral(model.filter(
                                iri, vf.createIRI("http://vvo.pisanoapi.at/salesFrom"), null))
                                .map(Literal::getLabel).orElse(null)) : null;
                XMLGregorianCalendar verkaufsoffenBis = Models.objectLiteral(model.filter(iri, vf.createIRI(
                        "http://vvo.pisanoapi.at/salesTo"), null)).map(Literal::getLabel).isPresent() ?
                        DatatypeFactory.newInstance().newXMLGregorianCalendar( Models.objectLiteral(model.filter(
                                iri, vf.createIRI("http://vvo.pisanoapi.at/salesTo"), null))
                                .map(Literal::getLabel).orElse(null)) : null;

                Optional<String> parentId = Models.objectResource(model.filter(iri, vf.createIRI(
                        "http://vvo.pisanoapi.at/Parent"), null)).map(Resource::stringValue);

                APrioriProduktbausteinType verkaufsproduktType;
                if (parentId.isPresent()) {
                    verkaufsproduktType = new APrioriUnterbausteinType();
                } else {
                    verkaufsproduktType = new APrioriVerkaufsproduktType();
                }

                for (Pair<String, AttributType> attribute : attributeListe) {
                    if (attribute.getKey().equals(id)){
                        verkaufsproduktType.getAttribute().add(attribute.getValue());
                    }
                }

                verkaufsproduktType.setId(iri.stringValue().substring("http://vvo.pisanoapi.at/ProdElement".length(), iri.stringValue().indexOf("-") > 0 ? iri.stringValue().indexOf("-") : iri.stringValue().length()));
                verkaufsproduktType.setName(bez);
                verkaufsproduktType.setType(type);
                verkaufsproduktType.setFrom(verkaufsoffenVon);
                verkaufsproduktType.setTo(verkaufsoffenBis);
                verkaufsproduktType.setMaxOccurrences(maxOccurrence);
                verkaufsproduktType.setMinOccurrences(minOccurrence);
                verkaufsproduktType.setRisikoobjektType(risikoobjektType);
                verkaufsproduktType.setPraemienfaktor(praemie);

                parentId.ifPresent(p -> parents.put(id, p));
                bausteine.put(id, verkaufsproduktType);
            }
        }

        List<APrioriVerkaufsproduktType> verkaufsprodukteList = new ArrayList<>();

        for (Map.Entry<String, APrioriProduktbausteinType> entry : bausteine.entrySet()){
            String id = entry.getKey();
            Object baustein = entry.getValue();
            String parentId = parents.get(id);

            if (parentId != null && bausteine.containsKey(parentId)){
                bausteine.get(parentId).getBausteine().add((APrioriUnterbausteinType) baustein);
            }else {
                verkaufsprodukteList.add((APrioriVerkaufsproduktType) baustein);
            }
        }

        return verkaufsprodukteList;
    }

    public VerkaufsproduktType getCalculateRDF(String r) throws DatatypeConfigurationException, IOException {
        return calculateRequestToVerkaufsprodukt(restClient.post()
                .uri("http://localhost:9090/CalculateRequest")
                .contentType(MediaType.APPLICATION_JSON)
                .body(r)
                .retrieve()
                .toEntity(String.class)
                .getBody());
    }

    public VerkaufsproduktType calculateRequestToVerkaufsprodukt(String calculateRequest) throws IOException, DatatypeConfigurationException {
        Model model = Rio.parse(new StringReader(calculateRequest), "", RDFFormat.JSONLD);

        Map<String, ProduktbausteinType> bausteine = new LinkedHashMap<>();
        Map<String, String> parents = new HashMap<>();

        List<Pair<String, AttributType>> attListe = getAttributesFromRequest(calculateRequest);
        List<Pair<String, FahrzeugType>> risikoObjektListe = getRisikoObjekteFromRequest(calculateRequest);

        List<Resource> subjects = new ArrayList<>(Models.subjectIRIs(model));
        Collections.reverse(subjects);

        for (Resource iri : subjects) {
            if (iri.stringValue().contains("Prod")) {
                String id = iri.stringValue();

                ValueFactory vf = SimpleValueFactory.getInstance();

                int insId = Integer.parseInt(Models.objectLiteral(model.filter(iri, vf.createIRI(
                        "http://vvo.pisanoapi.at/ins_id"), null)).map(Literal::getLabel).orElse("-1"));
                int maxOccurrence = Integer.parseInt(Models.objectLiteral(model.filter(iri, vf.createIRI(
                        "http://vvo.pisanoapi.at/maxOccurrence"), null)).map(Literal::getLabel).orElse("-1"));
                int minOccurrence = Integer.parseInt(Models.objectLiteral(model.filter(iri, vf.createIRI(
                        "http://vvo.pisanoapi.at/minOccurrence"), null)).map(Literal::getLabel).orElse("-1"));
                String bez = Models.objectLiteral(model.filter(iri, vf.createIRI(
                        "http://vvo.pisanoapi.at/bez"), null)).map(Literal::getLabel).orElse(null);
                String type = Models.objectLiteral(model.filter(iri, vf.createIRI(
                        "http://vvo.pisanoapi.at/type"), null)).map(Literal::getLabel).orElse(null);
                String risikoobjektType = Models.objectLiteral(model.filter(iri, vf.createIRI(
                        "http://vvo.pisanoapi.at/risikoobjektType"), null)).map(Literal::getLabel).orElse(null);
                String praemie = Models.objectLiteral(model.filter(iri, vf.createIRI(
                        "http://vvo.pisanoapi.at/praemienfaktor"), null)).map(Literal::getLabel).orElse(null);
                List<ServiceFault> meldungen = model.filter(iri, vf.createIRI(
                        "http://vvo.pisanoapi.at/Meldung"), null).objects().stream()
                        .filter(s -> s instanceof IRI).map(s -> {
                            IRI spezMeldung = (IRI) s;

                            String msg = Models.objectLiteral(model.filter(spezMeldung, vf.createIRI("http://vvo.pisanoapi.at/errorMsg"), null)).map(Literal::getLabel).orElse(null);
                            String errorType = Models.objectLiteral(model.filter(spezMeldung, vf.createIRI("http://vvo.pisanoapi.at/errorType"), null)).map(Literal::getLabel).orElse(null);

                            ServiceFault serviceFault = new ServiceFault();

                            serviceFault.setErrorMsg(msg);
                            serviceFault.setErrorType(BigInteger.valueOf(Integer.parseInt("1")));

                            return serviceFault;
                        }).toList();
                XMLGregorianCalendar verkaufsoffenVon = Models.objectLiteral(model.filter(iri, vf.createIRI(
                        "http://vvo.pisanoapi.at/salesFrom"), null)).map(Literal::getLabel).isPresent() ? DatatypeFactory.newInstance().newXMLGregorianCalendar(
                        Models.objectLiteral(model.filter(iri, vf.createIRI(
                                "http://vvo.pisanoapi.at/salesFrom"), null)).map(Literal::getLabel).orElse(null)) : null;
                XMLGregorianCalendar verkaufsoffenBis = Models.objectLiteral(model.filter(iri, vf.createIRI(
                        "http://vvo.pisanoapi.at/salesTo"), null)).map(Literal::getLabel).isPresent() ? DatatypeFactory.newInstance().newXMLGregorianCalendar(
                        Models.objectLiteral(model.filter(iri, vf.createIRI(
                                "http://vvo.pisanoapi.at/salesTo"), null)).map(Literal::getLabel).orElse(null)) : null;
                List<String> fahrzeuge = model.filter(iri, vf.createIRI(
                                "http://vvo.pisanoapi.at/VersichertesInteresseType"), null).objects().stream()
                        .filter(s -> s instanceof IRI).map(Value::stringValue).toList();

                Optional<String> parentId = Models.objectResource(model.filter(iri, vf.createIRI(
                        "http://vvo.pisanoapi.at/Parent"), null)).map(Resource::stringValue);
                if (parentId.isEmpty()) {
                    parentId = Models.objectLiteral(model.filter(iri, vf.createIRI(
                            "http://vvo.pisanoapi.at/Parent"), null)).map(Literal::getLabel);
                }

                ObjectFactoryFactory off = new ObjectFactoryFactory();

                ProduktbausteinType verkaufsproduktType = off.create(type);

                verkaufsproduktType.setId(iri.stringValue().substring("http://vvo.pisanoapi.at/ProdElement".length(), iri.stringValue().indexOf("-") > 0 ? iri.stringValue().indexOf("-") : iri.stringValue().length()));
                verkaufsproduktType.setBezeichnung(bez);
                verkaufsproduktType.setTyp(type);
                verkaufsproduktType.setVerkaufsoffenVon(verkaufsoffenVon);
                verkaufsproduktType.setVerkaufsoffenBis(verkaufsoffenBis);
                verkaufsproduktType.setMaxVorkommen(maxOccurrence);
                verkaufsproduktType.setMinVorkommen(minOccurrence);
                verkaufsproduktType.setRisikoobjektErforderlich(risikoobjektType != null);
                verkaufsproduktType.setPraemienfaktor(praemie);

                for (ServiceFault meldung : meldungen) {
                    if (meldung != null) {
                        ServiceFault tmp = new ServiceFault();
                        tmp.setErrorMsg(meldung.getErrorMsg());
                        tmp.setErrorType(meldung.getErrorType());
                        verkaufsproduktType.getMeldungen().add(tmp);
                    }
                }

                for (Pair<String, FahrzeugType> pair : risikoObjektListe) {
                    for (String fahrzeugId : fahrzeuge) {
                        if (pair.getKey().equals(fahrzeugId)) {
                            verkaufsproduktType.getVersicherteObjekte().add(pair.getValue());
                        }
                    }
                }

                for (Pair<String, AttributType> pair : attListe) {
                    if (pair.getKey().equals(id)) {
                        verkaufsproduktType.getAttribute().add(pair.getValue());
                    }
                }

                parentId.ifPresent(p -> parents.put(id, p));
                bausteine.put(id, verkaufsproduktType);
            }
        }

        List<VerkaufsproduktType> verkaufsprodukteList = new ArrayList<>();


        for (Map.Entry<String, ProduktbausteinType> entry : bausteine.entrySet()){
            String id = entry.getKey();
            ProduktbausteinType baustein = entry.getValue();
            String parentId = parents.get(id);

            if (parentId != null && bausteine.containsKey(parentId)){
                bausteine.get(parentId).getBausteine().add(baustein);
            }else {
                verkaufsprodukteList.add((VerkaufsproduktType) baustein);
            }
        }

        return verkaufsprodukteList.getFirst();
    }

    private List<Pair<String, FahrzeugType>> getRisikoObjekteFromRequest(String calculateRequest) throws IOException, DatatypeConfigurationException {
        Model model = Rio.parse(new StringReader(calculateRequest), "", RDFFormat.JSONLD);
        List<Pair<String, FahrzeugType>> erg = new ArrayList<>();

        String baseIri = "http://vvo.pisanoapi.at/";
        IRI bezIri = iri(baseIri + "bez");
        IRI baujahrIri = iri(baseIri + "baujahr");
        IRI erstzulassungIri = iri(baseIri + "erstzulassung");
        IRI kenIri = iri(baseIri + "kennzeichen");
        IRI leistungIri = iri(baseIri + "leistung");
        IRI sonderIri = iri(baseIri + "sonderausstattung");
        IRI listenpreisIri = iri(baseIri + "listenpreis");


        for (Resource iri : Models.subjectIRIs(model)) {
            if (iri.stringValue().contains("Fahrzeug")) {
                String id = iri.stringValue();

                String bez = Models.objectLiteral(
                        model.filter(iri, bezIri, null)).map(Literal::getLabel).orElse(null);
                String ken = Models.objectLiteral(
                        model.filter(iri, kenIri, null)).map(Literal::getLabel).orElse(null);
                BigDecimal listenpreis = BigDecimal.valueOf(
                        Double.parseDouble(Models.objectLiteral(model.filter(iri, listenpreisIri, null))
                                .map(Literal::getLabel).orElse("-1")));
                BigDecimal sonderausstattung = BigDecimal.valueOf(
                        Double.parseDouble(Models.objectLiteral(model.filter(iri, sonderIri, null))
                                .map(Literal::getLabel).orElse("-1")));

                int leistung = Integer.parseInt(Models.objectLiteral(
                        model.filter(iri, leistungIri, null)).map(Literal::getLabel).orElse("-1"));
                int baujahr = Integer.parseInt(Models.objectLiteral(
                        model.filter(iri, baujahrIri, null)).map(Literal::getLabel).orElse("-1"));

                XMLGregorianCalendar erstzulassung = Models.objectLiteral(model.filter(
                        iri, erstzulassungIri, null)).map(Literal::getLabel).isPresent() ?
                        DatatypeFactory.newInstance().newXMLGregorianCalendar(
                        Models.objectLiteral(model.filter(iri, erstzulassungIri, null))
                                .map(Literal::getLabel).orElse(null)) : null;

                FahrzeugType fahrzeug = new FahrzeugType();

                fahrzeug.setErstzulassdat(erstzulassung);
                fahrzeug.setBaujahr(baujahr);
                fahrzeug.setHandelsbez(bez);
                fahrzeug.setPolKennz(ken);
                fahrzeug.setLeistung(leistung);
                fahrzeug.setListenpreis(listenpreis);
                fahrzeug.setSonderausstattung(sonderausstattung);

                erg.add(new Pair<>(id, fahrzeug));
            }
        }
        return erg;
    }

    public Model createRdfModel(TreeItem<GuiProdukt> vp) {
        Model erg = new LinkedHashModel();
        String baseIri = "http://vvo.pisanoapi.at/";

        Map<String, Integer> idCount = new LinkedHashMap<>();
        createIdCount(idCount, vp);

        Map<TreeItem<GuiProdukt>, IRI> iriMap = new LinkedHashMap<>();
        createIriMap(vp, baseIri, idCount, iriMap);

        addProduktToModel(vp, erg, baseIri, null, iriMap);

        return erg;
    }

    private void createIdCount(Map<String, Integer> idCount, TreeItem<GuiProdukt> vp) {
        if (!idCount.containsKey(((ProduktbausteinType) vp.getValue().getProdukt()).getId())) {
            idCount.put(((ProduktbausteinType) vp.getValue().getProdukt()).getId(), 1);
        }else {
            idCount.put(((ProduktbausteinType) vp.getValue().getProdukt()).getId(), idCount.get(((ProduktbausteinType) vp.getValue().getProdukt()).getId()) + 1);
        }

        for (TreeItem<GuiProdukt> child : vp.getChildren()){
            createIdCount(idCount, child);
        }

    }

    private void createIriMap(TreeItem<GuiProdukt> produkt, String baseIri, Map<String, Integer> idCount, Map<TreeItem<GuiProdukt>, IRI> iriMap) {
        IRI baseElemIri = iri(baseIri + "ProdElement");

        for (int i = 1; i < idCount.get(((ProduktbausteinType)produkt.getValue().getProdukt()).getId())+1; i++) {
            IRI tmpIri = iri(baseElemIri + ((ProduktbausteinType) produkt.getValue().getProdukt()).getId() + "-" + i);

            if (!iriMap.containsValue(tmpIri)) {
                iriMap.put(produkt, tmpIri);
                break;
            }
        }

        for (TreeItem<GuiProdukt> child : produkt.getChildren()){
            createIriMap(child, baseIri, idCount, iriMap);
        }

    }

    public void addProduktToModel(TreeItem<GuiProdukt> guiProdukt, Model model, String baseIri, String parentId, Map<TreeItem<GuiProdukt>, IRI> iriMap) {
        ProduktbausteinType produkt = (ProduktbausteinType) guiProdukt.getValue().getProdukt();

        IRI prodelemIri = iri(baseIri + "ProdElement");
        IRI insIdIri = iri(baseIri + "ins_id");
        IRI bezIri = iri(baseIri + "bez");
        IRI createdIri = iri(baseIri + "created");
        IRI salesFromIri = iri(baseIri + "salesFrom");
        IRI salesToIri = iri(baseIri + "salesTo");
        IRI minOccurrenceIri = iri(baseIri + "minOccurrence");
        IRI maxOccurrenceIri = iri(baseIri + "maxOccurrence");
        IRI typeIri = iri(baseIri + "type");
        IRI risikoobjektTypeIri = iri(baseIri + "risikoobjektType");
        IRI bausteinIri = iri(baseIri + "Baustein");
        IRI parentIri = iri(baseIri + "Parent");
        IRI praemieIri = iri(baseIri + "praemienfaktor");

        IRI vpIri = iriMap.get(guiProdukt);

        if (!guiProdukt.getValue().isNotIncluded()) {
            model.add(vpIri, RDF.TYPE, prodelemIri);
            model.add(vpIri, bezIri, Values.literal(produkt.getBezeichnung()));
            if (produkt.getVerkaufsoffenVon() != null)
                model.add(vpIri, salesFromIri, Values.literal(produkt.getVerkaufsoffenVon()));
            if (produkt.getVerkaufsoffenBis() != null)
                model.add(vpIri, salesToIri, Values.literal(produkt.getVerkaufsoffenBis()));
            if (produkt.getMinVorkommen() != null)
                model.add(vpIri, minOccurrenceIri, Values.literal(produkt.getMinVorkommen()));
            if (produkt.getMaxVorkommen() != null)
                model.add(vpIri, maxOccurrenceIri, Values.literal(produkt.getMaxVorkommen()));
            model.add(vpIri, typeIri, Values.literal(produkt.getTyp()));
            if (produkt.isRisikoobjektErforderlich()) model.add(vpIri, risikoobjektTypeIri, Values.literal("FahrzeugType"));
            if (produkt.getPraemienfaktor() != null) model.add(vpIri, praemieIri, Values.literal(produkt.getPraemienfaktor()));
            if (parentId != null) model.add(vpIri, parentIri, Values.iri(parentId));

        }

        for (AttributType attribut : produkt.getAttribute()){
            IRI atBez = iri(baseIri + "bez");
            IRI atRequiredIri = iri(baseIri + "required");
            IRI atProd = iri(baseIri + "ProdElement");
            IRI atValue = iri(baseIri + "value");
            IRI atMax = iri(baseIri + "max");
            IRI atMin = iri(baseIri + "min");
            IRI atDefault = iri(baseIri + "default");
            IRI atAenderbar = iri(baseIri + "aenderbar");

            if (attribut instanceof AttributIntType){
                IRI specificAt = iri(baseIri + "ElemInt" + attribut.getId());

                model.add(specificAt, RDF.TYPE, iri(baseIri + "ElemInt"));
                model.add(specificAt, atBez, Values.literal(attribut.getBezeichnung()));
                model.add(specificAt, atRequiredIri, Values.literal(((AttributIntType) attribut).isPflichtfeld()));
                if (((AttributIntType) attribut).getValue() != null) model.add(specificAt, atValue, Values.literal(((AttributIntType) attribut).getValue()));
                model.add(specificAt, atProd, vpIri);
                model.add(specificAt, atMax, Values.literal(((AttributIntType) attribut).getMax()));
                model.add(specificAt, atMin, Values.literal(((AttributIntType) attribut).getMin()));
                model.add(specificAt, atDefault, Values.literal(((AttributIntType) attribut).getDefault()));
                model.add(specificAt, atAenderbar, Values.literal(attribut.isAenderbar() != null ? attribut.isAenderbar().toString() : true));

            } else if (attribut instanceof AttributBooleanType) {
                IRI specificAt = iri(baseIri + "ElemBoolean" + attribut.getId());

                model.add(specificAt, RDF.TYPE, iri(baseIri + "ElemBoolean"));
                model.add(specificAt, atBez, Values.literal(attribut.getBezeichnung()));
                model.add(specificAt, atRequiredIri, Values.literal(((AttributBooleanType) attribut).isPflichtfeld()));
                if (((AttributBooleanType) attribut).isValue() != null) model.add(specificAt, atValue, Values.literal(((AttributBooleanType) attribut).isValue()));
                model.add(specificAt, atProd, vpIri);
                model.add(specificAt, atDefault, Values.literal(((AttributBooleanType) attribut).isDefault()));

            } else if (attribut instanceof AttributStringType) {
                IRI specificAt = iri(baseIri + "ElemString" + attribut.getId());

                model.add(specificAt, RDF.TYPE, iri(baseIri + "ElemString"));
                model.add(specificAt, atBez, Values.literal(attribut.getBezeichnung()));
                model.add(specificAt, atRequiredIri, Values.literal(((AttributStringType) attribut).isPflichtfeld()));
                if (((AttributStringType) attribut).getValue() != null) model.add(specificAt, atValue, Values.literal(((AttributStringType) attribut).getValue()));
                model.add(specificAt, atProd, vpIri);
                model.add(specificAt, atDefault, Values.literal(((AttributStringType) attribut).getDefault()));

            } else if (attribut instanceof AttributDezimalType) {
                IRI specificAt = iri(baseIri + "ElemDecimal" + attribut.getId());

                model.add(specificAt, RDF.TYPE, iri(baseIri + "ElemDecimal"));
                model.add(specificAt, atBez, Values.literal(attribut.getBezeichnung()));
                model.add(specificAt, atRequiredIri, Values.literal(((AttributDezimalType) attribut).isPflichtfeld()));
                if (((AttributDezimalType) attribut).getValue() != null) model.add(specificAt, atValue, Values.literal(((AttributDezimalType) attribut).getValue()));
                model.add(specificAt, atProd, vpIri);
                if (((AttributDezimalType) attribut).getMax() != null) model.add(specificAt, atMax, Values.literal(((AttributDezimalType) attribut).getMax()));
                if (((AttributDezimalType) attribut).getMin() != null) model.add(specificAt, atMin, Values.literal(((AttributDezimalType) attribut).getMin()));
                if (((AttributDezimalType) attribut).getDefault() != null) model.add(specificAt, atDefault, Values.literal(((AttributDezimalType) attribut).getDefault()));
            }
        }

        int meldungId = 1;
        for (ServiceFault sf : produkt.getMeldungen()){
            IRI meldung = iri(baseIri + "Meldung");
            IRI spezMeldung = iri(baseIri + "Meldung" + meldungId);
            IRI meldungMsg = iri(baseIri + "errorMsg");
            IRI meldungType = iri(baseIri + "errorType");

            model.add(vpIri, meldung, spezMeldung);
            model.add(spezMeldung, meldungMsg, Values.literal(sf.getErrorMsg()));
            model.add(spezMeldung, meldungType, Values.literal(sf.getErrorType()));

            meldungId++;
        }

        for (VersichertesInteresseType ro : produkt.getVersicherteObjekte()){
            if (ro instanceof FahrzeugType fa){
                IRI fahrzeugIri = iri(baseIri + "FahrzeugType");
                IRI faIri = iri(fahrzeugIri.stringValue() + fa.getHandelsbez() + fa.getBaujahr());
                IRI faBaujahrIri = iri(baseIri + "baujahr");
                IRI faBezIri = iri(baseIri + "bez");
                IRI faErstZuIri = iri(baseIri + "erstzulassung");
                IRI faKennIri = iri(baseIri + "kennzeichen");
                IRI faLpreisIri = iri(baseIri + "listenpreis");
                IRI faSonderIri = iri(baseIri + "sonderausstattung");
                IRI faLeistungIri = iri(baseIri + "leistung");
                IRI prodRoIri = iri(baseIri + "VersichertesInteresseType");

                model.add(faIri, RDF.TYPE, fahrzeugIri);
                model.add(faIri, faBaujahrIri, Values.literal(fa.getBaujahr()));
                model.add(faIri, faBezIri, Values.literal(fa.getHandelsbez()));
                model.add(faIri, faErstZuIri, Values.literal(fa.getErstzulassdat()));
                model.add(faIri, faKennIri, Values.literal(fa.getPolKennz()));
                model.add(faIri, faLpreisIri, Values.literal(fa.getListenpreis()));
                model.add(faIri, faSonderIri, Values.literal(fa.getSonderausstattung()));
                model.add(faIri, faLeistungIri, Values.literal(fa.getLeistung()));

                model.add(vpIri, prodRoIri, faIri);

            } else if (ro instanceof  VersicherteLiegenschaftType) {
                //To-Do
            } else if (ro instanceof RisikoGebaeudeType) {
                //To-Do
            }else if (ro instanceof RisikoHaushaltType) {
                //To-Do
            }else if (ro instanceof VersichertePersonType) {
                //To-Do
            }else if (ro instanceof VersichertesObjektSachPrivatType) {
                //To-Do
            }
        }

        for (int i = 0; i < guiProdukt.getChildren().size(); i++) {
            IRI unterbausteinIri = iriMap.get(guiProdukt.getChildren().get(i));
            if (!guiProdukt.getValue().isNotIncluded()) {
                model.add(vpIri, bausteinIri, unterbausteinIri);
            }

            String newParentId = vpIri.stringValue();
            addProduktToModel(guiProdukt.getChildren().get(i), model, baseIri, newParentId, iriMap);
        }
    }
}
