package com.correx.core.transitions.conditions

import com.correx.core.events.types.ArtifactId
import com.correx.core.transitions.evaluation.EvaluationContext
import com.correx.core.transitions.graph.TransitionCondition
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive

enum class FieldOperator(val symbol: String) {
    EQ("eq"),
    NEQ("neq"),
    GT("gt"),
    GTE("gte"),
    LT("lt"),
    LTE("lte");

    companion object {
        fun fromString(s: String): FieldOperator =
            entries.firstOrNull { it.symbol == s }
                ?: error("Unknown field operator '$s'. Supported: ${entries.joinToString(", ") { it.symbol }}")
    }
}

data class ArtifactFieldEquals(
    val artifactId: ArtifactId,
    val field: String,
    val value: String,
    val operator: FieldOperator = FieldOperator.EQ,
) : TransitionCondition {

    override fun evaluate(context: EvaluationContext): Boolean {
        val contentJson = context.artifactContent[artifactId]
            ?: error("Artifact '${artifactId.value}' not found in evaluation context")

        val element = Json.parseToJsonElement(contentJson)
        val resolved = resolveField(element, field)
            ?: error("Field '$field' not found in artifact '${artifactId.value}' content. " +
                    "Available top-level fields: ${topLevelFields(element)}")

        return compareValues(resolved, value, operator)
    }

    @Suppress("ReturnCount")
    private fun resolveField(element: JsonElement, fieldPath: String): JsonPrimitive? {
        var current: JsonElement? = element
        for (part in fieldPath.split('.')) {
            current = (current as? JsonObject)?.get(part)
            if (current == null) return null
        }
        return current as? JsonPrimitive
    }

    private fun compareValues(actual: JsonPrimitive, expected: String, op: FieldOperator): Boolean = when (op) {
        FieldOperator.EQ -> actual.content == expected
        FieldOperator.NEQ -> actual.content != expected
        FieldOperator.GT, FieldOperator.GTE, FieldOperator.LT, FieldOperator.LTE -> {
            val actualNum = actual.content.toDoubleOrNull()
                ?: error("Numeric operator '${op.symbol}' applied to non-numeric field value '${actual.content}'")
            val expectedNum = expected.toDoubleOrNull()
                ?: error("Numeric operator '${op.symbol}' requires numeric expected value, got '$expected'")
            when (op) {
                FieldOperator.GT -> actualNum > expectedNum
                FieldOperator.GTE -> actualNum >= expectedNum
                FieldOperator.LT -> actualNum < expectedNum
                FieldOperator.LTE -> actualNum <= expectedNum
                else -> false
            }
        }
    }

    private fun topLevelFields(element: JsonElement): String =
        when (element) {
            is JsonObject -> element.keys.joinToString(", ")
            else -> "<not an object>"
        }
}
