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.graphics.drawscope.Stroke
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 line chart
 */
data class ChartDataPoint(
    val x: Float,
    val y: Float,
    val label: String? = null,
)

/**
 * Configuration for chart styling
 */
data class ChartStyle(
    val lineColor: Color,
    val fillColor: Color,
    val lineWidth: Float = 3f,
    val gridColor: Color,
    val textColor: Color,
    val backgroundColor: Color,
)

/**
 * Custom Line Chart with area fill below the line
 */
@Composable
fun LineChart(
    data: List<ChartDataPoint>,
    modifier: Modifier = Modifier,
    style: ChartStyle = ChartStyle(
        lineColor = MaterialTheme.colorScheme.primary,
        fillColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.2f),
        gridColor = MaterialTheme.colorScheme.outline.copy(alpha = 0.3f),
        textColor = MaterialTheme.colorScheme.onSurfaceVariant,
        backgroundColor = MaterialTheme.colorScheme.surface,
    ),
    showGrid: Boolean = true,
    xAxisFormatter: (Float) -> String = { it.toString() },
    yAxisFormatter: (Float) -> String = { it.toString() },
) {
    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

            // Calculate data bounds
            val dataMinY = data.minOf { it.y }
            val dataMaxY = data.maxOf { it.y }

            // Add some padding to Y-axis (10% above and below the data range)
            val yPadding = if (dataMaxY == dataMinY) 1f else (dataMaxY - dataMinY) * 0.1f
            val minY = dataMinY - yPadding
            val maxY = dataMaxY + yPadding

            val minX = data.minOf { it.x }
            val maxX = data.maxOf { it.x }

            val xRange = if (maxX - minX == 0f) 1f else maxX - minX // Minimum range of 1 for single points
            val yRange = maxY - minY

            // Ensure we have valid ranges
            if (yRange == 0f) return@Canvas

            // Convert data points to screen coordinates
            val screenPoints = data.map { point ->
                val x = padding + (point.x - minX) / xRange * chartWidth
                val y = padding + chartHeight - (point.y - minY) / yRange * chartHeight
                Offset(x, y)
            }

            // 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,
                    minX = minX,
                    maxX = maxX,
                    minY = minY,
                    maxY = maxY,
                    textMeasurer = textMeasurer,
                    textColor = style.textColor,
                    xAxisFormatter = xAxisFormatter,
                    yAxisFormatter = yAxisFormatter,
                    actualDataPoints = data,
                )
            }

            // Draw area fill
            if (screenPoints.size > 1) {
                drawAreaFill(
                    points = screenPoints,
                    padding = padding,
                    chartHeight = chartHeight,
                    fillColor = style.fillColor,
                )
            }

            // Draw line
            if (screenPoints.size > 1) {
                drawLine(
                    points = screenPoints,
                    lineColor = style.lineColor,
                    lineWidth = style.lineWidth,
                )
            }

            // Draw data points - more pronounced
            screenPoints.forEach { point ->
                // Draw outer circle (larger)
                drawCircle(
                    color = style.lineColor,
                    radius = 8f,
                    center = point,
                )
                // Draw inner circle (white center)
                drawCircle(
                    color = style.backgroundColor,
                    radius = 5f,
                    center = point,
                )
                // Draw border for better visibility
                drawCircle(
                    color = style.lineColor,
                    radius = 8f,
                    center = point,
                    style = Stroke(width = 2f),
                )
            }
        }
    }
}

private fun DrawScope.drawGrid(
    padding: Float,
    chartWidth: Float,
    chartHeight: Float,
    gridColor: Color,
    minX: Float,
    maxX: Float,
    minY: Float,
    maxY: Float,
    textMeasurer: TextMeasurer,
    textColor: Color,
    xAxisFormatter: (Float) -> String,
    yAxisFormatter: (Float) -> String,
    actualDataPoints: List<ChartDataPoint>,
) {
    val textStyle = TextStyle(
        color = textColor,
        fontSize = 10.sp,
    )

    // Draw vertical grid lines (X-axis) - only at integer values for sessions
    val xRange = maxX - minX
    if (xRange > 0) {
        val startX = kotlin.math.ceil(minX).toInt()
        val endX = kotlin.math.floor(maxX).toInt()

        for (sessionNum in startX..endX) {
            val x = padding + (sessionNum.toFloat() - minX) / xRange * chartWidth

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

            // X-axis labels removed per user request
        }
    }

    // Draw horizontal grid lines (Y-axis) - only at actual data point values
    val yRange = maxY - minY
    if (yRange > 0) {
        // Get unique Y values from actual data points
        val actualYValues = actualDataPoints.map { kotlin.math.round(it.y).toInt() }.toSet()

        actualYValues.forEach { gradeValue ->
            val y = padding + chartHeight - (gradeValue.toFloat() - minY) / yRange * chartHeight

            // Only draw if within chart bounds
            if (y >= padding && y <= padding + chartHeight) {
                // Draw grid line
                drawLine(
                    color = gridColor,
                    start = Offset(padding, y),
                    end = Offset(padding + chartWidth, y),
                    strokeWidth = 1.dp.toPx(),
                )

                // Draw label
                val text = yAxisFormatter(gradeValue.toFloat())
                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,
                    ),
                )
            }
        }
    }
}

private fun DrawScope.drawAreaFill(
    points: List<Offset>,
    padding: Float,
    chartHeight: Float,
    fillColor: Color,
) {
    val bottomY = padding + chartHeight // This represents the bottom of the chart area

    val path = Path().apply {
        // Start from bottom-left (at chart bottom level)
        moveTo(points.first().x, bottomY)

        // Draw to first point
        lineTo(points.first().x, points.first().y)

        // Draw line through all points
        for (i in 1 until points.size) {
            lineTo(points[i].x, points[i].y)
        }

        // Close the path by going to bottom-right (at chart bottom level) and back to start
        lineTo(points.last().x, bottomY)
        lineTo(points.first().x, bottomY)
        close()
    }

    drawPath(
        path = path,
        color = fillColor,
    )
}

private fun DrawScope.drawLine(
    points: List<Offset>,
    lineColor: Color,
    lineWidth: Float,
) {
    val path = Path().apply {
        moveTo(points.first().x, points.first().y)
        for (i in 1 until points.size) {
            lineTo(points[i].x, points[i].y)
        }
    }

    drawPath(
        path = path,
        color = lineColor,
        style = Stroke(
            width = lineWidth,
            cap = StrokeCap.Round,
            join = StrokeJoin.Round,
        ),
    )
}
