package com.atridad.ascently.ui.components

import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.*
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.TextMeasurer
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.drawText
import androidx.compose.ui.text.rememberTextMeasurer
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp

/** Data point for the bar chart */
data class BarChartDataPoint(val label: String, val value: Int, val gradeNumeric: Int)

/** Configuration for bar chart styling */
data class BarChartStyle(
    val barColor: Color,
    val gridColor: Color,
    val textColor: Color,
    val backgroundColor: Color,
)

/** Custom Bar Chart for displaying grade distribution */
@Composable
fun BarChart(
    data: List<BarChartDataPoint>,
    modifier: Modifier = Modifier,
    style: BarChartStyle =
        BarChartStyle(
            barColor = MaterialTheme.colorScheme.primary,
            gridColor = MaterialTheme.colorScheme.outline.copy(alpha = 0.3f),
            textColor = MaterialTheme.colorScheme.onSurfaceVariant,
            backgroundColor = MaterialTheme.colorScheme.surface,
        ),
    showGrid: Boolean = true,
) {
    val textMeasurer = rememberTextMeasurer()
    val density = LocalDensity.current

    Box(modifier = modifier) {
        Canvas(modifier = Modifier.fillMaxSize().padding(16.dp)) {
            if (data.isEmpty()) return@Canvas

            val padding = with(density) { 32.dp.toPx() }
            val chartWidth = size.width - padding * 2
            val chartHeight = size.height - padding * 2

            // Sort data by grade numeric value
            val sortedData = data.sortedBy { it.gradeNumeric }

            // Calculate max value
            val maxValue = sortedData.maxOfOrNull { it.value } ?: 1

            // Bar dimensions
            val barCount = sortedData.size
            val totalSpacing = chartWidth * 0.2f
            val barSpacing = if (barCount > 1) totalSpacing / (barCount + 1) else totalSpacing / 2
            val barWidth = (chartWidth - totalSpacing) / barCount

            // Draw background
            drawRect(
                color = style.backgroundColor,
                topLeft = Offset(padding, padding),
                size = androidx.compose.ui.geometry.Size(chartWidth, chartHeight),
            )

            // Draw grid
            if (showGrid) {
                drawGrid(
                    padding = padding,
                    chartWidth = chartWidth,
                    chartHeight = chartHeight,
                    gridColor = style.gridColor,
                    maxValue = maxValue,
                    textMeasurer = textMeasurer,
                    textColor = style.textColor,
                )
            }

            // Draw bars and labels
            sortedData.forEachIndexed { index, dataPoint ->
                val barHeight =
                    if (maxValue > 0) {
                        (dataPoint.value.toFloat() / maxValue.toFloat()) * chartHeight * 0.8f
                    } else {
                        0f
                    }

                val barX =
                    padding +
                        barSpacing +
                        index * (barWidth + barSpacing / (barCount - 1).coerceAtLeast(1))
                val barY = padding + chartHeight - barHeight

                // Draw bar
                drawRect(
                    color = style.barColor,
                    topLeft = Offset(barX, barY),
                    size = androidx.compose.ui.geometry.Size(barWidth, barHeight),
                )

                // Draw value on bar
                if (dataPoint.value > 0) {
                    val valueText = dataPoint.value.toString()
                    val textStyle = TextStyle(color = style.textColor, fontSize = 10.sp)
                    val textSize = textMeasurer.measure(valueText, textStyle)

                    // Position text
                    val textY =
                        if (barHeight > textSize.size.height + 8.dp.toPx()) {
                            barY + 8.dp.toPx()
                        } else {
                            barY - 4.dp.toPx()
                        }

                    val textColor =
                        if (barHeight > textSize.size.height + 8.dp.toPx()) {
                            Color.White
                        } else {
                            style.textColor
                        }

                    drawText(
                        textMeasurer = textMeasurer,
                        text = valueText,
                        style = textStyle.copy(color = textColor),
                        topLeft = Offset(barX + barWidth / 2f - textSize.size.width / 2f, textY),
                    )
                }

                // Draw grade label below bar
                val gradeText = dataPoint.label
                val labelTextStyle = TextStyle(color = style.textColor, fontSize = 10.sp)
                val labelTextSize = textMeasurer.measure(gradeText, labelTextStyle)

                drawText(
                    textMeasurer = textMeasurer,
                    text = gradeText,
                    style = labelTextStyle,
                    topLeft =
                    Offset(
                        barX + barWidth / 2f - labelTextSize.size.width / 2f,
                        padding + chartHeight + 8.dp.toPx(),
                    ),
                )
            }
        }
    }
}

private fun DrawScope.drawGrid(
    padding: Float,
    chartWidth: Float,
    chartHeight: Float,
    gridColor: Color,
    maxValue: Int,
    textMeasurer: TextMeasurer,
    textColor: Color,
) {
    val textStyle = TextStyle(color = textColor, fontSize = 10.sp)

    // Horizontal grid lines
    val gridLines =
        when {
            maxValue <= 5 -> (0..maxValue).toList()
            maxValue <= 10 -> (0..maxValue step 2).toList()
            maxValue <= 20 -> (0..maxValue step 5).toList()
            else -> {
                val step = (maxValue / 5).coerceAtLeast(1)
                (0..maxValue step step).toList()
            }
        }

    gridLines.forEach { value ->
        val y = padding + chartHeight - (value.toFloat() / maxValue.toFloat()) * chartHeight * 0.8f

        // Draw grid line
        drawLine(
            color = gridColor,
            start = Offset(padding, y),
            end = Offset(padding + chartWidth, y),
            strokeWidth = 1.dp.toPx(),
        )

        // Draw Y-axis label
        if (value >= 0) {
            val text = value.toString()
            val textSize = textMeasurer.measure(text, textStyle)
            drawText(
                textMeasurer = textMeasurer,
                text = text,
                style = textStyle,
                topLeft =
                Offset(
                    padding - textSize.size.width - 8.dp.toPx(),
                    y - textSize.size.height / 2f,
                ),
            )
        }
    }
}
