package com.developx.szpitale.load;

import org.neo4j.driver.Driver;
import org.neo4j.driver.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;

public class GraphValidator {

    private static final Logger log = LoggerFactory.getLogger(GraphValidator.class);
    private final Driver driver;

    public GraphValidator(Driver driver) {
        this.driver = driver;
    }

    public ValidationReport validate() {
        ValidationReport report = new ValidationReport();
        try (Session session = driver.session()) {

            // 1. Hospitals without directors
            var result1 = session.run(
                    "MATCH (h:Hospital) WHERE NOT (h)-[:DYREKTOR]->() " +
                    "RETURN h.name AS hospital, h.city AS city, h.voivodeship AS voivodeship " +
                    "ORDER BY h.voivodeship, h.name");
            List<Map<String, Object>> hospitalsWithoutDirector = new ArrayList<>();
            result1.list().forEach(r -> hospitalsWithoutDirector.add(r.asMap()));
            report.setHospitalsWithoutDirector(hospitalsWithoutDirector);

            // 2. People with mandates linking to hospital roles
            var result2 = session.run(
                    "MATCH (h:Hospital)-[r:CZLONEK_ORGANU|PRZEWODNICZY_ORGANOWI]->(p:Person)-[:PELNI_MANDAT]->(g:GovBody) " +
                    "RETURN p.fullName AS person, " +
                    "       collect(DISTINCT h.name) AS szpitale, " +
                    "       collect(DISTINCT g.name) AS mandaty, " +
                    "       collect(DISTINCT labels(p)) AS labels " +
                    "ORDER BY person");
            List<Map<String, Object>> overlaps = new ArrayList<>();
            result2.list().forEach(r -> overlaps.add(r.asMap()));
            report.setOverlapReport(overlaps);

            // 3. People in multiple hospitals
            var result3 = session.run(
                    "MATCH (h:Hospital)-[r:CZLONEK_ORGANU|PRZEWODNICZY_ORGANOWI]->(p:Person) " +
                    "WITH p, collect(DISTINCT h.name) AS szpitale WHERE size(szpitale) > 1 " +
                    "RETURN p.fullName AS person, szpitale, count(p) AS connections " +
                    "ORDER BY connections DESC");
            List<Map<String, Object>> multiHospital = new ArrayList<>();
            result3.list().forEach(r -> multiHospital.add(r.asMap()));
            report.setMultiHospitalReport(multiHospital);

            // 4. Overall counts
            var nodeCountResult = session.run(
                    "MATCH (n) RETURN labels(n)[0] AS label, count(*) AS count ORDER BY count DESC");
            Map<String, Long> nodeCounts = new HashMap<>();
            nodeCountResult.list().forEach(r ->
                    nodeCounts.put(r.get("label").asString(), r.get("count").asLong()));
            report.setNodeCounts(nodeCounts);

            var relCountResult = session.run(
                    "MATCH ()-[rel]->() RETURN type(rel) AS type, count(*) AS count ORDER BY count DESC");
            Map<String, Long> relCounts = new HashMap<>();
            relCountResult.list().forEach(r ->
                    relCounts.put(r.get("type").asString(), r.get("count").asLong()));
            report.setRelCounts(relCounts);

            var totalNodes = session.run("MATCH (n) RETURN count(*) AS total").single().get("total").asLong();
            report.setTotalNodes(totalNodes);

            var totalRels = session.run("MATCH ()-[rel]->() RETURN count(*) AS total").single().get("total").asLong();
            report.setTotalRelationships(totalRels);
        }
        return report;
    }

    public void exportOverlapCsv(Path outputPath, List<Map<String, Object>> overlaps) throws IOException {
        StringBuilder csv = new StringBuilder();
        csv.append("osoba;szpitale;mandaty;afiliacje;confidence;sourceUrls\n");
        for (Map<String, Object> row : overlaps) {
            String person = (String) row.getOrDefault("person", "");
            List<?> szpitale = (List<?>) row.getOrDefault("szpitale", List.of());
            List<?> mandaty = (List<?>) row.getOrDefault("mandaty", List.of());
            csv.append(person)
               .append(";").append(String.join(", ", szpitale.stream().map(Object::toString).toList()))
               .append(";").append(String.join(", ", mandaty.stream().map(Object::toString).toList()))
               .append("\n");
        }
        Files.writeString(outputPath, csv.toString());
        log.info("Overlap report written to: {}", outputPath);
    }

    public static class ValidationReport {
        public List<Map<String, Object>> hospitalsWithoutDirector = new ArrayList<>();
        public List<Map<String, Object>> overlapReport = new ArrayList<>();
        public List<Map<String, Object>> multiHospitalReport = new ArrayList<>();
        public Map<String, Long> nodeCounts = new HashMap<>();
        public Map<String, Long> relCounts = new HashMap<>();
        public long totalNodes;
        public long totalRelationships;

        public void setHospitalsWithoutDirector(List<Map<String, Object>> list) { this.hospitalsWithoutDirector = list; }
        public void setOverlapReport(List<Map<String, Object>> list) { this.overlapReport = list; }
        public void setMultiHospitalReport(List<Map<String, Object>> list) { this.multiHospitalReport = list; }
        public void setNodeCounts(Map<String, Long> map) { this.nodeCounts = map; }
        public void setRelCounts(Map<String, Long> map) { this.relCounts = map; }
        public void setTotalNodes(long n) { this.totalNodes = n; }
        public void setTotalRelationships(long r) { this.totalRelationships = r; }

        @Override
        public String toString() {
            return "Validation report:\n" +
                    "  Total nodes: " + totalNodes + "\n" +
                    "  Total relationships: " + totalRelationships + "\n" +
                    "  Node types: " + nodeCounts + "\n" +
                    "  Relationship types: " + relCounts + "\n" +
                    "  Hospitals without directors: " + hospitalsWithoutDirector.size() + "\n" +
                    "  People with mandates: " + overlapReport.size() + "\n" +
                    "  People in multiple hospitals: " + multiHospitalReport.size() + "\n";
        }
    }
}
