package kotlinx.schema.json.conformance

import kotlin.math.roundToInt

/**
 * Generates spec coverage reports from round-trip test results.
 */
object SpecCoverageReportGenerator {
    private val json = kotlinx.serialization.json.Json { prettyPrint = true }

    /**
     * Generates a markdown report from test file statistics.
     *
     * @param fileStats Map of file names to their test statistics
     * @return Markdown-formatted report as a string
     */
    fun generateMarkdown(fileStats: Map<String, FileStats>): String =
        buildString {
            val stats = OverallStats.from(fileStats.values)

            appendOverallStatistics(stats)
            appendResultsByFile(fileStats)
            appendFailureAnalysis(fileStats)
        }

    private fun StringBuilder.appendOverallStatistics(stats: OverallStats) {
        appendLine("# 📊 JSON Schema Draft 2020-12 Spec Coverage Report")
        appendLine()
        appendLine("Generated by conformance tests for `JsonSchema`.")
        appendLine()

        appendLine("## 📈 Overall Statistics")
        appendLine()
        appendLine("- **Total Test Files**: ${stats.totalFiles}")
        appendLine("- **Total Schemas Tested**: ${stats.totalSchemas}")
        appendLine("- **✅ Successfully Parsed**: ${stats.totalSuccess}")
        appendLine("- **❌ Failed to Parse**: ${stats.totalFailures}")
        appendLine("- **Success Rate**: ${stats.successRate.roundToInt()}%")
        appendLine()
    }

    private fun StringBuilder.appendResultsByFile(fileStats: Map<String, FileStats>) {
        appendLine("## 📁 Results by Test File")
        appendLine()
        appendLine("| File | Success Rate | ✅ Passed | ❌ Failed |")
        appendLine("|------|--------------|----------|----------|")

        fileStats.values
            .sortedBy { it.fileName }
            .forEach { stats ->
                val rate = stats.successRate.roundToInt()
                appendLine("| ${stats.fileName} | $rate% | ${stats.successCount} | ${stats.failureCount} |")
            }
        appendLine()
    }

    private fun StringBuilder.appendFailureAnalysis(fileStats: Map<String, FileStats>) {
        appendLine("## 🔍 Failure Analysis")
        appendLine()

        val allFailures = fileStats.values.flatMap { it.failures }

        appendFailuresByStage(allFailures)
        appendTopFailureTypes(allFailures)
        appendDetailedFailures(fileStats)
    }

    private fun StringBuilder.appendFailuresByStage(failures: List<SchemaTestResult>) {
        appendLine("### Failures by Stage")
        appendLine()

        failures
            .groupBy { it.stage ?: "unknown" }
            .forEach { (stage, stageFailures) ->
                appendLine("- **$stage**: ${stageFailures.size} failures")
            }
        appendLine()
    }

    private fun StringBuilder.appendTopFailureTypes(failures: List<SchemaTestResult>) {
        appendLine("### 🏷️ Top Failure Types")
        appendLine()

        failures
            .mapNotNull { it.error }
            .groupBy { it::class.simpleName }
            .entries
            .sortedByDescending { it.value.size }
            .take(TOP_FAILURE_TYPES_LIMIT)
            .forEach { (errorType, errorFailures) ->
                appendLine("- **$errorType**: ${errorFailures.size} occurrences")
            }
        appendLine()
    }

    private fun StringBuilder.appendDetailedFailures(fileStats: Map<String, FileStats>) {
        appendLine("## 📋 Detailed Failures")
        appendLine()

        fileStats.values
            .filter { it.failures.isNotEmpty() }
            .sortedBy { it.fileName }
            .forEach { stats ->
                appendLine("### ${stats.fileName} (${stats.failureCount} failed)")
                appendLine()

                stats.failures
                    .sortedBy { it.testSuiteDescription }
                    .forEach { failure ->
                        appendFailureItem(failure)
                    }
            }
    }

    private fun StringBuilder.appendFailureItem(failure: SchemaTestResult) {
        val stage = failure.stage ?: "unknown"
        appendLine("#### ❌ **${failure.testSuiteDescription}** [$stage]")
        appendLine()

        // Show exception details
        failure.error?.let { error ->
            appendErrorDetails(error)
        }

        // Show the schema that failed
        failure.schema?.let { schema ->
            appendLine("**Schema:**")
            appendLine("```json")
            appendLine(formatJson(schema))
            appendLine("```\n")
        }
        appendLine()
    }

    private fun StringBuilder.appendErrorDetails(error: Throwable) {
        appendLine("**Error:**\n")
        appendLine("```")
        appendLine("${error::class.simpleName}: ${error.message ?: error.toString()}")

        // Include cause if present
        error.cause?.let { cause ->
            appendLine()
            appendLine(
                "  Caused by: ${cause::class.simpleName}: ${cause.message ?: cause.toString()}",
            )
        }

        appendLine("```\n")
    }

    private fun formatJson(element: kotlinx.serialization.json.JsonElement): String =
        json
            .encodeToString(
                kotlinx.serialization.json.JsonElement
                    .serializer(),
                element,
            ).lines()
            .joinToString("\n") { "  $it" }

    private const val TOP_FAILURE_TYPES_LIMIT = 10
}
