@file:Suppress("HardCodedStringLiteral")

package eu.gaudian.translator.utils

import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerializationException
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.buildJsonArray
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.jsonPrimitive

/**
 * Enhanced JSON helper with unified parsing, validation, and error handling.
 * Provides robust JSON processing for all API responses in the application.
 */
class JsonHelper {
    
    private val jsonParser = Json {
        ignoreUnknownKeys = true
        isLenient = true
        coerceInputValues = true
        encodeDefaults = false
    }
    
    /**
     * Parses a JSON string into the specified type with comprehensive error handling.
     * 
     * @param json The JSON string to parse
     * @param serializer The serializer for the target type
     * @param serviceName Name of the service calling this method for logging
     * @return Result containing the parsed object or error
     */
    fun <T> parseJson(
        json: String,
        serializer: KSerializer<T>,
        serviceName: String = "Unknown"
    ): Result<T> {
        return try {
            Log.d("JsonHelper", "[$serviceName] Parsing JSON response")
            val cleanedJson = cleanAndValidateJson(json)
            Log.d("JsonHelper", "[$serviceName] Cleaned JSON: $cleanedJson")
            
            val result = jsonParser.decodeFromString(serializer, cleanedJson)
            Log.i("JsonHelper", "[$serviceName] Successfully parsed JSON response")
            Result.success(result)
        } catch (e: SerializationException) {
            val errorMsg = "JSON serialization failed in $serviceName: ${e.message}"
            Log.e("JsonHelper", errorMsg, e)
            Result.failure(JsonParsingException(errorMsg, e))
        } catch (e: Exception) {
            val errorMsg = "Unexpected error parsing JSON in $serviceName: ${e.message}"
            Log.e("JsonHelper", errorMsg, e)
            Result.failure(JsonParsingException(errorMsg, e))
        }
    }
    
    /**
     * Cleans and validates a JSON string, handling common formatting issues.
     */
    fun cleanAndValidateJson(json: String): String {
        if (json.isBlank()) {
            throw IllegalArgumentException("JSON string is blank")
        }
        
        // Use existing JsonCleanUtil for robust cleaning
        return JsonCleanUtil.cleanAndCorrectJson(json) { message, exception ->
            Log.w("JsonHelper", "JSON cleaning issue: $message", exception?.cause)
        }
    }
    
    /**
     * Validates that a JSON string contains expected required fields.
     */
    fun validateRequiredFields(json: String, requiredFields: List<String>): Boolean {
        return try {
            val element = jsonParser.parseToJsonElement(json)
            if (element is JsonObject) {
                requiredFields.all { field -> element.containsKey(field) }
            } else {
                false
            }
        } catch (e: Exception) {
            Log.e("JsonHelper", "Failed to validate required fields: ${e.message}", e)
            false
        }
    }
    
    /**
     * Extracts a specific field value from JSON as string.
     */
    fun extractField(json: String, fieldName: String): String? {
        return try {
            val element = jsonParser.parseToJsonElement(json)
            if (element is JsonObject) {
                element[fieldName]?.jsonPrimitive?.content
            } else {
                null
            }
        } catch (e: Exception) {
            Log.e("JsonHelper", "Failed to extract field '$fieldName': ${e.message}", e)
            null
        }
    }
    
    /**
     * Formats JSON for display purposes.
     */
    fun formatForDisplay(json: String): String {
        return try {
            val element = jsonParser.parseToJsonElement(json)
            jsonParser.encodeToString(JsonElement.serializer(), element)
                .replace(",", ",\n")
                .replace("{", "{\n")
                .replace("}", "\n}")
                .replace("[", "[\n")
                .replace("]", "\n]")
        } catch (e: Exception) {
            Log.w("JsonHelper", "Failed to format JSON for display: ${e.message}")
            json
        }
    }
}

/**
 * Custom exception for JSON parsing errors.
 */
class JsonParsingException(message: String, cause: Throwable? = null) : Exception(message, cause)

object JsonCleanUtil {
    private val jsonParser = Json { isLenient = true; ignoreUnknownKeys = true }

    /**
     * Cleans and corrects a raw string response to make it valid, parsable JSON.
     */
    fun cleanAndCorrectJson(
        response: String,
        onFailure: ((message: String, exception: Exception?) -> Unit)? = null
    ): String {
        //Log.d("JsonCleanUtil", "Response: $response")
        //Isolate the primary JSON block.
        val jsonBlock = isolateJsonBlock(response).replace("<|END_RESPONSE|>", "")
        if (jsonBlock.isBlank()) {
            onFailure?.invoke("No JSON block found in response", null)
            return ""
        }

        val sanitizedBlock = stripJsonCommentsAndFixCommonErrors(jsonBlock)
        Log.d("JsonCleanUtil", onFailure.toString())
        return correctPropertyValues(sanitizedBlock, onFailure)
    }

    private fun isolateJsonBlock(response: String): String {
        // Handle specific non-JSON tokens first

        // Strip any <think>...</think> blocks (may contain newlines)
        val thinkRegex = Regex("<think>[\\s\\S]*?</think>")
        val cleanedResponse = thinkRegex.replace(response, "").trim()

        // The rest of the function operates on the cleaned response
        val markdownRegex = Regex("```json\\s*([\\s\\S]*?)\\s*```")
        val markdownMatch = markdownRegex.find(cleanedResponse)
        if (markdownMatch != null && markdownMatch.groupValues.size > 1) {
            return markdownMatch.groupValues[1]
        }

        val firstBrace = cleanedResponse.indexOf('{')
        val firstBracket = cleanedResponse.indexOf('[')
        val startIndex = when {
            firstBrace == -1 -> firstBracket
            firstBracket == -1 -> firstBrace
            else -> minOf(firstBrace, firstBracket)
        }
        if (startIndex == -1) return ""

        val lastBrace = cleanedResponse.lastIndexOf('}')
        val lastBracket = cleanedResponse.lastIndexOf(']')
        val endIndex = maxOf(lastBrace, lastBracket)
        if (endIndex == -1 || startIndex >= endIndex) return ""

        return cleanedResponse.substring(startIndex, endIndex + 1)
    }

    /**
     * A more robust function to remove comments, stray characters, and fix common structural errors like trailing commas.
     */
    private fun stripJsonCommentsAndFixCommonErrors(jsonString: String): String {
        val lines = jsonString.lines()
        val cleanedLines = mutableListOf<String>()

        for (line in lines) {
            // Discard lines that are primarily comments
            if (line.trim().startsWith("//")) {
                continue
            }
            // Find the position of a line comment and take the substring before it
            val commentPosition = line.indexOf("//")
            val cleanLine = if (commentPosition != -1) {
                line.take(commentPosition)
            } else {
                line
            }

            // Remove other problematic characters, like the stray '/' from the log
            //cleanLine = cleanLine.replace("/", "")

            cleanedLines.add(cleanLine)
        }

        var result = cleanedLines.joinToString("\n")


        @Suppress("RegExpRedundantEscape")
        result = result.replace(Regex(",(\\s*[\\]}])"), "$1")

        return result
    }

    private fun correctPropertyValues(
        jsonString: String,
        onFailure: ((message: String, exception: Exception?) -> Unit)?
    ): String {
        try {
            val rootElement = jsonParser.parseToJsonElement(jsonString)
            if (rootElement !is JsonObject) return jsonString

            val resultsArray = rootElement["results"] as? JsonArray ?: return jsonString
            val correctedResults = buildJsonArray {
                for (element in resultsArray) {
                    if (element is JsonObject) {
                        val properties = element["properties"] as? JsonObject
                        if (properties != null) {
                            val correctedProperties = buildJsonObject {
                                for ((key, value) in properties) {
                                    // If the AI returns a value in an array, just take the first element.
                                    if (value is JsonArray && value.isNotEmpty()) {
                                        put(key, value.first())
                                    } else {
                                        put(key, value)
                                    }
                                }
                            }
                            val correctedElement = buildJsonObject {
                                // Safely add properties that exist in the original element
                                element["id"]?.let { put("id", it) }
                                element["wordClass"]?.let { put("wordClass", it) }
                                put("properties", correctedProperties)
                            }
                            add(correctedElement)
                        } else {
                            add(element)
                        }
                    } else {
                        add(element)
                    }
                }
            }

            return buildJsonObject { put("results", correctedResults) }.toString()
        } catch (e: Exception) {
            Log.e("JsonCleanUtil", "Failed during JSON correction: ${e.message}")
            onFailure?.invoke("Failed during JSON correction: ${e.message}", e)
            return jsonString
        }
    }
}

fun formatJsonForDisplay(json: String): String {
    return try {
        // Simple JSON formatting - you can enhance this later
        json.replace(",", ",\n").replace("{", "{\n").replace("}", "\n}").replace("[", "[\n").replace("]", "\n]")
    } catch (_: Exception) {
        json // Fallback to raw JSON if formatting fails
    }
}
