package eu.gaudian.translator.view.vocabulary

import android.annotation.SuppressLint
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProgressIndicatorDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.navigation.NavController
import eu.gaudian.translator.R
import eu.gaudian.translator.utils.findActivity
import eu.gaudian.translator.view.composable.AppCard
import eu.gaudian.translator.view.composable.AppIcons
import eu.gaudian.translator.view.composable.AppScaffold
import eu.gaudian.translator.view.composable.AppTopAppBar
import eu.gaudian.translator.viewmodel.CategoryProgress
import eu.gaudian.translator.viewmodel.ProgressViewModel
import kotlinx.coroutines.launch
import kotlinx.datetime.DateTimeUnit
import kotlinx.datetime.LocalDate
import kotlinx.datetime.TimeZone
import kotlinx.datetime.minus
import kotlinx.datetime.number
import kotlinx.datetime.plus
import kotlinx.datetime.todayIn
import java.time.YearMonth
import java.time.format.DateTimeFormatter
import java.util.Locale.getDefault
import kotlin.math.log2
import kotlin.time.Clock
import kotlin.time.ExperimentalTime

// Data class to hold the data for a single month's grid
private data class HeatmapMonth(val yearMonth: YearMonth, val weeks: List<List<LocalDate?>>)

@OptIn(ExperimentalTime::class)
@Composable
fun VocabularyHeatmapScreen(
    navController: NavController
) {
    val activity = LocalContext.current.findActivity()
    val viewModel : ProgressViewModel = hiltViewModel(activity)

    // State and derived data
    val dailyStats by viewModel.dailyVocabularyStats.collectAsState()
    val today = remember { Clock.System.todayIn(TimeZone.currentSystemDefault()) }
    val categoryProgressList by viewModel.categoryProgressList.collectAsState()
    val streak by viewModel.streak.collectAsState()
    val totalWordsCompleted by viewModel.totalWordsCompleted.collectAsState()
    val totalWordsInProgress by viewModel.totalWordsInProgress.collectAsState()
    val startDate = remember { today.minus(12, DateTimeUnit.MONTH) }

    AppScaffold(
        topBar = {
            AppTopAppBar(
                title = stringResource(R.string.label_vocabulary_activity),
                onNavigateBack = { navController.popBackStack() },
            )
        },
        modifier = Modifier.fillMaxSize()
    ) { paddingValues ->
        Column(
            // Allow content to scroll if it exceeds the screen height
            modifier = Modifier
                .verticalScroll(rememberScrollState())
                .padding(paddingValues)
                .padding(16.dp),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Column(
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
            // Heatmap
            HeatmapCalendar(
                dailyStats = dailyStats,
                startDate = startDate,
                endDate = today,
            )

            // Spacer and Legend
            Spacer(modifier = Modifier.height(24.dp))
            Legend(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(horizontal = 8.dp)
            )
            Spacer(modifier = Modifier.height(24.dp))
            CategoryProgressSection(
                categoryProgress = categoryProgressList,
            )
            Spacer(modifier = Modifier.height(24.dp))
            StatsOverview(
                streak = streak,
                wordsCompleted = totalWordsCompleted,
                wordsInProgress = totalWordsInProgress,
            )
            }
        }
    }
}

/**
 * Generates a list of HeatmapMonth objects, each containing the data for one month's grid.
 */
private fun generateHeatmapMonths(startDate: LocalDate, endDate: LocalDate): List<HeatmapMonth> {
    val months = mutableListOf<HeatmapMonth>()
    val startMonth = YearMonth.of(startDate.year, startDate.month.number)
    val endMonth = YearMonth.of(endDate.year, endDate.month.number)

    var currentMonth = startMonth
    while (!currentMonth.isAfter(endMonth)) {
        months.add(HeatmapMonth(
            yearMonth = currentMonth,
            weeks = generateWeeksForMonth(currentMonth)
        ))
        currentMonth = currentMonth.plusMonths(1)
    }
    return months
}

/**
 * Generates the list of weeks for a single month, with padding for alignment.
 */
private fun generateWeeksForMonth(yearMonth: YearMonth): List<List<LocalDate?>> {
    val firstDayOfMonth = yearMonth.atDay(1)
    val lastDayOfMonth = yearMonth.atEndOfMonth()
    val days = mutableListOf<LocalDate?>()

    val startDayOfWeek = firstDayOfMonth.dayOfWeek.value
    repeat(startDayOfWeek - 1) { days.add(null) }

    for (day in 1..lastDayOfMonth.dayOfMonth) {
        val date = yearMonth.atDay(day)
        days.add(LocalDate(date.year, date.monthValue, date.dayOfMonth))
    }

    return days.chunked(7)
}


@SuppressLint("FrequentlyChangingValue")
@Composable
private fun HeatmapCalendar(
    dailyStats: Map<LocalDate, Int>,
    startDate: LocalDate,
    endDate: LocalDate,
    modifier: Modifier = Modifier
) {
    val months = remember(startDate, endDate) { generateHeatmapMonths(startDate, endDate) }
    val maxCount = remember(dailyStats) { dailyStats.values.maxOrNull() ?: 1 }
    val coroutineScope = rememberCoroutineScope()

    val lazyListState = rememberLazyListState(initialFirstVisibleItemIndex = months.size - 1)
    var currentMonth by remember { mutableStateOf(months.last().yearMonth) }

    LaunchedEffect(lazyListState.firstVisibleItemIndex) {
        if (months.isNotEmpty() && lazyListState.firstVisibleItemIndex < months.size) {
        currentMonth = months[lazyListState.firstVisibleItemIndex].yearMonth
    }
    }

    Column(modifier = modifier) {
        MonthHeader(
            month = currentMonth,
            onPrev = {
                coroutineScope.launch {
                    if (lazyListState.firstVisibleItemIndex > 0) {
                        lazyListState.animateScrollToItem(lazyListState.firstVisibleItemIndex - 1)
                    }
                }
            },
            onNext = {
                coroutineScope.launch {
                    if (lazyListState.firstVisibleItemIndex < months.size - 1) {
                        lazyListState.animateScrollToItem(lazyListState.firstVisibleItemIndex + 1)
                    }
                }
            }
        )
        Spacer(modifier = Modifier.height(8.dp))

        // This BoxWithConstraints ensures each month grid gets the full width of the container.
        BoxWithConstraints {
            val itemWidth = this.maxWidth
            LazyRow(
                state = lazyListState,
                // The fling behavior makes the list snap to the start of each item.
                flingBehavior = rememberSnapFlingBehavior(lazyListState = lazyListState),
                horizontalArrangement = Arrangement.spacedBy(16.dp) // Space between months
            ) {
                items(months.size) { index ->
                    MonthGrid(
                        modifier = Modifier.width(itemWidth),
                        month = months[index],
                        dailyStats = dailyStats,
                        maxCount = maxCount
                    )
                }
            }
        }
    }
}

@Composable
private fun MonthHeader(
    month: YearMonth,
    onPrev: () -> Unit,
    onNext: () -> Unit,
    modifier: Modifier = Modifier
) {
    val formatter = remember {
        @Suppress("HardCodedStringLiteral")
        DateTimeFormatter.ofPattern("MMMM yyyy")
    }
    Row(
        modifier = modifier.fillMaxWidth(),
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.Center
    ) {
        IconButton(onClick = onPrev) {
            Icon(AppIcons.ArrowLeft, contentDescription = stringResource(R.string.previous_month))
        }
        Text(
            text = month.format(formatter)
                .replaceFirstChar { if (it.isLowerCase()) it.titlecase(getDefault()) else it.toString() },
            style = MaterialTheme.typography.titleMedium,
            fontWeight = FontWeight.Bold,
            modifier = Modifier.padding(horizontal = 16.dp)
        )
        IconButton(onClick = onNext) {
            Icon(AppIcons.ArrowRight, contentDescription = stringResource(R.string.next_month))
        }
    }
}

@Composable
private fun MonthGrid(
    month: HeatmapMonth,
    dailyStats: Map<LocalDate, Int>,
    maxCount: Int,
    modifier: Modifier = Modifier
) {
    Column(modifier = modifier) {
        Row(modifier = Modifier.fillMaxWidth()) {
            val locale = getDefault()
            // Generate localized short weekday labels for Monday to Sunday.
            val dayFormatter = remember(locale) {
                DateTimeFormatter.ofPattern("EEEEE", locale)
            }
            val baseMonday = java.time.LocalDate.of(2024, 9, 2) // a known Monday
            val days = (0..6).map { offset ->
                val d = baseMonday.plusDays(offset.toLong())
                // Use 1-letter if available; otherwise fallback to short name and take first char
                val label = d.format(dayFormatter)
                if (label.length <= 2) label else label.first().toString()
            }
            days.forEach { day ->
                Text(
                    text = day,
                    style = MaterialTheme.typography.bodySmall,
                    modifier = Modifier.weight(1f),
                    textAlign = TextAlign.Center
                )
            }
        }
        Spacer(modifier = Modifier.height(4.dp))

        month.weeks.forEach { week ->
            Row(modifier = Modifier.fillMaxWidth()) {
                for (i in 0 until 7) {
                    val date = week.getOrNull(i)
                    if (date != null) {
                        val count = dailyStats[date] ?: 0
                        val colorIntensity = if (count > 0) {
                            (log2(count.toFloat() + 1) / log2(maxCount.toFloat() + 1))
                        } else {
                            0f
                        }.coerceIn(0f, 1f)

                        DaySquare(
                            date = date,
                            count = count,
                            colorIntensity = colorIntensity,
                            modifier = Modifier
                                .weight(1f)
                                .aspectRatio(1f)
                        )
                    } else {
                        Spacer(modifier = Modifier
                            .weight(1f)
                            .aspectRatio(1f))
                    }
                }
            }
        }
    }
}


@OptIn(ExperimentalTime::class)
@Composable
private fun DaySquare(
    date: LocalDate,
    count: Int,
    colorIntensity: Float,
    modifier: Modifier = Modifier
) {
    val today = remember { Clock.System.todayIn(TimeZone.currentSystemDefault()) }
    val isToday = date == today
    val isFuture = date > today
    val baseColor = MaterialTheme.colorScheme.primary

    val backgroundColor = when {
        isFuture -> MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.1f) // Faint color for future dates
        colorIntensity > 0 -> baseColor.copy(alpha = (0.15f + (colorIntensity * 0.85f)).coerceIn(0.15f, 1f))
        else -> MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.3f) // For past/today with no activity
    }

    Box(
        modifier = modifier
            .padding(2.dp)
            .clip(MaterialTheme.shapes.extraSmall)
            .background(backgroundColor)
            .border(
                width = 1.dp,
                color = if (isToday) MaterialTheme.colorScheme.outline else Color.Transparent,
                shape = MaterialTheme.shapes.extraSmall
            ),
        contentAlignment = Alignment.Center
    ) {
        // Only show the count for past and present days
        if (count > 0 && !isFuture) {
            Text(
                text = count.toString(),
                fontSize = 8.sp,
                color = if (colorIntensity > 0.6f) Color.White else MaterialTheme.colorScheme.onSurface
            )
        }
    }
}

@Composable
private fun Legend(modifier: Modifier = Modifier) {
    Row(
        modifier = modifier,
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.Center
    ) {
        Text(
            text = stringResource(R.string.less),
            style = MaterialTheme.typography.bodySmall,
            modifier = Modifier.padding(end = 6.dp)
        )

        val baseColor = MaterialTheme.colorScheme.primary
        val shades = listOf(0.15f, 0.4f, 0.7f, 1.0f)
        shades.forEach { alpha ->
            Box(
                modifier = Modifier
                    .size(14.dp)
                    .clip(MaterialTheme.shapes.extraSmall)
                    .background(baseColor.copy(alpha = alpha))
                    .padding(end = 2.dp) // Add spacing between color boxes
            )
        }

        Text(
            text = stringResource(R.string.more),
            style = MaterialTheme.typography.bodySmall,
            modifier = Modifier.padding(start = 6.dp) // Increased spacing
        )
    }
}

// --- Previews for easy testing in Android Studio ---

@OptIn(ExperimentalTime::class)
@Preview(showBackground = true, widthDp = 380)
@Composable
fun VocabularyHeatmapScreenPreview() {
    val today = Clock.System.todayIn(TimeZone.currentSystemDefault())
    val startDate = today.minus(12, DateTimeUnit.MONTH)
    val sampleStats = buildMap {
        var currentDate = startDate
        while (currentDate <= today) {
            if ((1..10).random() > 2) {
                put(currentDate, (1..25).random())
            }
            currentDate = currentDate.plus(1, DateTimeUnit.DAY)
        }
    }

    Column(modifier = Modifier
        .fillMaxSize()
        .padding(16.dp)) {
        Text(
            text = stringResource(R.string.label_vocabulary_activity),
            style = MaterialTheme.typography.headlineMedium,
            modifier = Modifier.padding(bottom = 24.dp)
        )
        HeatmapCalendar(dailyStats = sampleStats, startDate = startDate, endDate = today)
        Spacer(modifier = Modifier.height(24.dp))
        Legend(modifier = Modifier.fillMaxWidth())
    }
}

@Composable
fun StatsOverview(
    streak: Int,
    wordsCompleted: Int,
    wordsInProgress: Int,
    modifier: Modifier = Modifier
) {
    Row(
        modifier = modifier.fillMaxWidth(),
        horizontalArrangement = Arrangement.spacedBy(16.dp)
    ) {
        StatCard(title = stringResource(R.string.current_streak), value = stringResource(
            R.string.days_2d,
            streak
        ), modifier = Modifier.weight(1f))
        StatCard(title = stringResource(R.string.words_completed), value = wordsCompleted.toString(), modifier = Modifier.weight(1f))
        StatCard(title = stringResource(R.string.text_in_progress), value = wordsInProgress.toString(), modifier = Modifier.weight(1f))
    }
}

@Composable
private fun StatCard(title: String, value: String, modifier: Modifier = Modifier) {
    AppCard(
        modifier = modifier.padding(0.dp),
    ) {
        Column(
            modifier = Modifier.padding(8.dp),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Text(
                text = title,
                style = MaterialTheme.typography.labelMedium,
                color = MaterialTheme.colorScheme.onSurfaceVariant
            )
            Spacer(modifier = Modifier.height(4.dp))
            Text(
                text = value,
                style = MaterialTheme.typography.titleLarge,
                fontWeight = FontWeight.Bold
            )
        }
    }
}


/**
 * A Composable to show a detailed list of progress per vocabulary category.
 *
 * How to use:
 * 1. Collect the category progress list from your ViewModel:
 * val categoryProgress by viewModel.categoryProgressList.collectAsState()
 *
 * 2. Call this composable in the main Column of your screen, likely below the StatsOverview:
 * CategoryProgressSection(categoryProgress = categoryProgress)
 */
@Composable
fun CategoryProgressSection(
    categoryProgress: List<CategoryProgress>,
    modifier: Modifier = Modifier
) {
    Column(modifier = modifier) {
        Text(
            text = stringResource(R.string.progress_by_category),
            style = MaterialTheme.typography.titleMedium,
            modifier = Modifier.padding(bottom = 8.dp, top = 24.dp)
        )
        categoryProgress.forEach { progress ->
            CategoryProgressItem(progress = progress)
            Spacer(modifier = Modifier.height(12.dp))
        }
    }
}

@Composable
private fun CategoryProgressItem(progress: CategoryProgress) {
    val progressPercentage = if (progress.totalItems > 0) {
        progress.itemsCompleted.toFloat() / progress.totalItems.toFloat()
    } else {
        0f
    }
    // Animate the progress bar's movement
    val animatedProgress by animateFloatAsState(
        targetValue = progressPercentage,
        animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec,
        label = "CategoryProgressAnimation"
    )

    Column {
        Row(modifier = Modifier.fillMaxWidth()) {
            Text(
                text = progress.vocabularyCategory.name,
                style = MaterialTheme.typography.bodyMedium,
                fontWeight = FontWeight.SemiBold
            )
            Spacer(modifier = Modifier.weight(1f))
            Text(
                text = "${progress.itemsCompleted} / ${progress.totalItems}",
                style = MaterialTheme.typography.bodySmall,
                color = MaterialTheme.colorScheme.onSurfaceVariant
            )
        }
        Spacer(modifier = Modifier.height(4.dp))
        LinearProgressIndicator(
            progress = { animatedProgress },
            modifier = Modifier
                .fillMaxWidth()
                .height(8.dp)
                .clip(MaterialTheme.shapes.small),
        )
    }
}