package com.kapdion.omds.productdefinitions.apriori;

import at.vvo.omds.types.omds3.r2025_05.common.ProductsRequest;

import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.Model;
import org.eclipse.rdf4j.model.impl.LinkedHashModel;
import org.eclipse.rdf4j.model.util.Values;
import org.eclipse.rdf4j.model.vocabulary.RDF;
import org.eclipse.rdf4j.model.vocabulary.XSD;
import org.eclipse.rdf4j.query.GraphQuery;
import org.eclipse.rdf4j.query.GraphQueryResult;
import org.eclipse.rdf4j.repository.Repository;
import org.eclipse.rdf4j.repository.RepositoryConnection;
import org.eclipse.rdf4j.repository.sail.SailRepository;
import org.eclipse.rdf4j.rio.RDFFormat;
import org.eclipse.rdf4j.rio.Rio;
import org.eclipse.rdf4j.sail.memory.MemoryStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.stereotype.Service;

import javax.xml.datatype.DatatypeConstants;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import java.io.*;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

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

public class AprioriService {
    PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();

    public String getProductsResponse(ProductsRequest productsRequest) {
        Repository repo = new SailRepository(new MemoryStore());
        repo.init();

        try (RepositoryConnection conn = repo.getConnection()) {
            Resource resource = resolver.getResource("classpath:data/prodelements.ttl");
            conn.add(resource.getInputStream(), "", RDFFormat.TURTLE);

            String queryString = """
                    PREFIX vvo: <http://vvo.pisanoapi.at/>
                    
                    CONSTRUCT {
                        ?prodelement vvo:ins_id ?ins_id ;
                                     vvo:bez ?bez ;
                                     vvo:created ?created ;
                                     vvo:salesFrom ?salesfrom ;
                                     vvo:salesTo ?salesto;
                                     vvo:minOccurrence ?minOccurrence ;
                                     vvo:maxOccurrence ?maxOccurrence ;
                                     vvo:type ?type ;
                                     vvo:Parent ?parent ;
                                     vvo:risikoobjektType ?risikoobjektType ;
                                     vvo:praemienfaktor ?praemie
                    }
                    WHERE {
                        ?prodelement a vvo:ProdElement .
                        OPTIONAL { ?prodelement vvo:ins_id ?ins_id . }
                        OPTIONAL { ?prodelement vvo:bez ?bez . }
                        OPTIONAL { ?prodelement vvo:created ?created . }
                        OPTIONAL { ?prodelement vvo:salesFrom ?salesfrom . }
                        OPTIONAL { ?prodelement vvo:salesTo ?salesto . }
                        OPTIONAL { ?prodelement vvo:minOccurrence ?minOccurrence . }
                        OPTIONAL { ?prodelement vvo:maxOccurrence ?maxOccurrence . }
                        OPTIONAL { ?prodelement vvo:type ?type . }
                        OPTIONAL { ?prodelement vvo:Parent ?parent . }
                        OPTIONAL { ?prodelement vvo:risikoobjektType ?risikoobjektType . }
                        OPTIONAL { ?prodelement vvo:praemienfaktor ?praemie . }
                        FILTER ( ?salesfrom < ?stichtag && (!BOUND(?salesto) || ?salesto > ?stichtag) )
                    }
                   
                    """;

            XMLGregorianCalendar stichtagFormated = DatatypeFactory.newInstance().newXMLGregorianCalendarDate(productsRequest.getStichtag().getYear(), productsRequest.getStichtag().getMonth(), productsRequest.getStichtag().getDay(), DatatypeConstants.FIELD_UNDEFINED);

            GraphQuery graphQuery = conn.prepareGraphQuery(queryString);
            graphQuery.setBinding("stichtag", conn.getValueFactory().createLiteral(stichtagFormated.toXMLFormat(), XSD.DATE));

            Model model = new LinkedHashModel();
            try (GraphQueryResult result = graphQuery.evaluate()) {
                result.forEach(model::add);
            }

            addPlausisToRequest(model);
            addAttributeToRequest(model);

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            Rio.write(model, baos, RDFFormat.JSONLD);
            return baos.toString();

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private void addAttributeToRequest(Model model) {
        addBoolAttribut(model);
        addStringAttribut(model);
        addIntAttribut(model);
        addDecimalAttribut(model);
    }

    private void addDecimalAttribut(Model model) {
        Repository repo = new SailRepository(new MemoryStore());
        repo.init();

        try (RepositoryConnection conn = repo.getConnection()) {
            Resource resource = resolver.getResource("classpath:data/Attribute.ttl");
            conn.add(resource.getInputStream(), "", RDFFormat.TURTLE);

            String queryString = """
                    PREFIX vvo: <http://vvo.pisanoapi.at/>
                    
                    CONSTRUCT {
                        ?attribut a vvo:ElemDecimal ;
                                     vvo:bez ?bez ;
                                     vvo:ProdElement ?ProdElement ;
                                     vvo:required ?required ;
                                     vvo:max ?max ;
                                     vvo:min ?min ;
                                     vvo:default ?default ;
                    }
                    WHERE {
                        ?attribut a vvo:ElemDecimal .
                        OPTIONAL { ?attribut vvo:bez ?bez . }
                        OPTIONAL { ?attribut vvo:ProdElement ?ProdElement . }
                        OPTIONAL { ?attribut vvo:required ?required . }
                        OPTIONAL { ?attribut vvo:max ?max . }
                        OPTIONAL { ?attribut vvo:min ?min . }
                        OPTIONAL { ?attribut vvo:default ?default . }
                    }
                    
                    """;

            GraphQuery graphQuery = conn.prepareGraphQuery(queryString);
//            graphQuery.setBinding("stichtag", conn.getValueFactory().createLiteral(stichtagFormated.toXMLFormat(), XSD.DATE));

            try (GraphQueryResult result = graphQuery.evaluate()) {
                result.forEach(model::add);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private void addIntAttribut(Model model) {
        Repository repo = new SailRepository(new MemoryStore());
        repo.init();

        try (RepositoryConnection conn = repo.getConnection()) {
            Resource resource = resolver.getResource("classpath:data/Attribute.ttl");
            conn.add(resource.getInputStream(), "", RDFFormat.TURTLE);

            String queryString = """
                    PREFIX vvo: <http://vvo.pisanoapi.at/>
                   
                   CONSTRUCT {
                        ?attribut a vvo:ElemInt ;
                                     vvo:bez ?bez ;
                                     vvo:ProdElement ?ProdElement ;
                                     vvo:required ?required ;
                                     vvo:max ?max ;
                                     vvo:min ?min ;
                                     vvo:default ?default ;
                    }
                    WHERE {
                        ?attribut a vvo:ElemInt .
                        OPTIONAL { ?attribut vvo:bez ?bez . }
                        OPTIONAL { ?attribut vvo:ProdElement ?ProdElement . }
                        OPTIONAL { ?attribut vvo:required ?required . }
                        OPTIONAL { ?attribut vvo:max ?max . }
                        OPTIONAL { ?attribut vvo:min ?min . }
                        OPTIONAL { ?attribut vvo:default ?default . }
                    }
                   
                   """;

            GraphQuery graphQuery = conn.prepareGraphQuery(queryString);
//            graphQuery.setBinding("stichtag", conn.getValueFactory().createLiteral(stichtagFormated.toXMLFormat(), XSD.DATE));

            try (GraphQueryResult result = graphQuery.evaluate()) {
                result.forEach(model::add);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private void addStringAttribut(Model model) {
        Repository repo = new SailRepository(new MemoryStore());
        repo.init();

        try (RepositoryConnection conn = repo.getConnection()) {
            Resource resource = resolver.getResource("classpath:data/Attribute.ttl");
            conn.add(resource.getInputStream(), "", RDFFormat.TURTLE);

            String queryString = """
                    PREFIX vvo: <http://vvo.pisanoapi.at/>
                    
                      CONSTRUCT {
                        ?attribut a vvo:ElemString ;
                                     vvo:bez ?bez ;
                                     vvo:ProdElement ?ProdElement ;
                                     vvo:required ?required ;
                                     vvo:default ?default ;
                    }
                    WHERE {
                        ?attribut a vvo:ElemString .
                        OPTIONAL { ?attribut vvo:bez ?bez . }
                        OPTIONAL { ?attribut vvo:ProdElement ?ProdElement . }
                        OPTIONAL { ?attribut vvo:required ?required . }
                        OPTIONAL { ?attribut vvo:default ?default . }
                    }
                    
                    """;

            GraphQuery graphQuery = conn.prepareGraphQuery(queryString);
//            graphQuery.setBinding("stichtag", conn.getValueFactory().createLiteral(stichtagFormated.toXMLFormat(), XSD.DATE));

            try (GraphQueryResult result = graphQuery.evaluate()) {
                result.forEach(model::add);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private void addBoolAttribut(Model model) {
        Repository repo = new SailRepository(new MemoryStore());
        repo.init();

        try (RepositoryConnection conn = repo.getConnection()) {
            Resource resource = resolver.getResource("classpath:data/Attribute.ttl");
            conn.add(resource.getInputStream(), "", RDFFormat.TURTLE);

            String queryString = """
                    PREFIX vvo: <http://vvo.pisanoapi.at/>
                    
                    CONSTRUCT {
                        ?attribut a vvo:ElemBoolean ;
                                     vvo:bez ?bez ;
                                     vvo:ProdElement ?ProdElement ;
                                     vvo:required ?required ;
                                     vvo:default ?default ;
                    }
                    WHERE {
                        ?attribut a vvo:ElemBoolean .
                        OPTIONAL { ?attribut vvo:bez ?bez . }
                        OPTIONAL { ?attribut vvo:ProdElement ?ProdElement . }
                        OPTIONAL { ?attribut vvo:required ?required . }
                        OPTIONAL { ?attribut vvo:default ?default . }
                    }
                    
                    """;

            GraphQuery graphQuery = conn.prepareGraphQuery(queryString);
//            graphQuery.setBinding("stichtag", conn.getValueFactory().createLiteral(stichtagFormated.toXMLFormat(), XSD.DATE));

            try (GraphQueryResult result = graphQuery.evaluate()) {
                result.forEach(model::add);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public void addPlausisToRequest(Model model) throws IOException {
        IRI plausiIri = iri("http://vvo.pisanoapi.at/Plausi");
        IRI queryIri = iri("http://vvo.pisanoapi.at/query");
        IRI beschreibungIri = iri("http://vvo.pisanoapi.at/beschreibung");
        IRI artIri = iri("http://vvo.pisanoapi.at/art");

        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource[] resources = resolver.getResources("classpath*:data/plausis/*");
        int i = 1;
        List<Resource> sortedResources = Arrays.stream(resources)
                .sorted(Comparator.comparing(Resource::getFilename))
                .toList();

        for (Resource plausiResource : sortedResources) {
            List<String> lines;
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(plausiResource.getInputStream()))) {
                lines = reader.lines().map(String::trim).toList();
            }

            String beschreibung = lines.stream()
                    .filter(line -> line.startsWith("#beschreibung:"))
                    .findFirst()
                    .map(line -> line.substring(14))
                    .orElse("Keine Beschreibung");

            String art = lines.stream()
                    .filter(line -> line.startsWith("#art:"))
                    .findFirst()
                    .map(line -> line.substring(5))
                    .orElse("Unbekannt");

            String query = lines.stream()
                    .filter(line -> !line.startsWith("#") && !line.isEmpty())
                    .collect(Collectors.joining("\n"));

            IRI spezPlausiIri = iri(plausiIri.stringValue() + i);

            model.add(spezPlausiIri, RDF.TYPE, plausiIri);
            model.add(spezPlausiIri, queryIri, Values.literal(query));
            model.add(spezPlausiIri, beschreibungIri, Values.literal(beschreibung));
            model.add(spezPlausiIri, artIri, Values.literal(art));

            i++;
        }
    }
}
