package de.arxsilex.maptools.converter.core.mtci

import de.arxsilex.maptools.converter.core.pbf.OsmPreviewRelation
import de.arxsilex.maptools.converter.core.pbf.OsmRelationMemberType
import java.io.IOException
import java.nio.file.Path

data class MtciIntegrityReport(
    val nodeCount: Long,
    val wayCount: Long,
    val relationCount: Long,
    val duplicateNodeIds: Long,
    val duplicateWayIds: Long,
    val duplicateRelationIds: Long,
    val missingWayNodeReferences: Long,
    val missingRelationMemberReferences: Long,
) {
    val valid: Boolean
        get() = duplicateNodeIds == 0L &&
            duplicateWayIds == 0L &&
            duplicateRelationIds == 0L &&
            missingWayNodeReferences == 0L &&
            missingRelationMemberReferences == 0L

    fun requireValid() {
        if (!valid) {
            throw IOException(
                "MTCI-Integritätsprüfung fehlgeschlagen: doppelte IDs " +
                    "N=$duplicateNodeIds/W=$duplicateWayIds/R=$duplicateRelationIds, " +
                    "fehlende Referenzen Ways=$missingWayNodeReferences/Relations=$missingRelationMemberReferences.",
            )
        }
    }
}

object MtciIntegrityValidator {
    fun validate(directory: Path): MtciIntegrityReport {
        val nodeIds = HashSet<Long>()
        val wayIds = HashSet<Long>()
        val relationIds = HashSet<Long>()
        val relations = ArrayList<OsmPreviewRelation>()
        var duplicateNodes = 0L
        var duplicateWays = 0L
        var duplicateRelations = 0L
        var missingWayNodes = 0L

        val nodeCount = MtciReader.forEachNode(directory) { node ->
            if (!nodeIds.add(node.id)) duplicateNodes++
        }
        val wayCount = MtciReader.forEachWay(directory) { way ->
            if (!wayIds.add(way.id)) duplicateWays++
            way.nodeIds.forEach { nodeId ->
                if (nodeId !in nodeIds) missingWayNodes++
            }
        }
        val relationCount = MtciReader.forEachRelation(directory) { relation ->
            if (!relationIds.add(relation.id)) duplicateRelations++
            relations += relation
        }
        var missingRelationMembers = 0L
        relations.forEach { relation ->
            relation.members.forEach { member ->
                val available = when (member.type) {
                    OsmRelationMemberType.NODE -> member.id in nodeIds
                    OsmRelationMemberType.WAY -> member.id in wayIds
                    OsmRelationMemberType.RELATION -> member.id in relationIds
                }
                if (!available) missingRelationMembers++
            }
        }
        return MtciIntegrityReport(
            nodeCount = nodeCount,
            wayCount = wayCount,
            relationCount = relationCount,
            duplicateNodeIds = duplicateNodes,
            duplicateWayIds = duplicateWays,
            duplicateRelationIds = duplicateRelations,
            missingWayNodeReferences = missingWayNodes,
            missingRelationMemberReferences = missingRelationMembers,
        )
    }
}
