package com.marcus.meditationtimer

import android.Manifest
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.widget.Toast
import androidx.core.content.FileProvider
import java.io.File
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
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.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.FilterChip
import androidx.compose.material3.FilterChipDefaults
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Tab
import androidx.compose.material3.TabRow
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.darkColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import kotlinx.coroutines.launch
import java.time.Instant
import java.time.LocalDate
import java.time.ZoneId
import java.time.format.DateTimeFormatter

private val Gold = Color(0xFFE3BC4B)
private val DeepBlue = Color(0xFF10202A)
private val Dim = Color.White.copy(alpha = 0.35f)

class MainActivity : ComponentActivity() {

    private val notifPermission =
        registerForActivityResult(ActivityResultContracts.RequestPermission()) {}

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        SessionStore.init(this)
        handleTokenIntent(intent)
        if (Build.VERSION.SDK_INT >= 33 &&
            checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED
        ) {
            notifPermission.launch(Manifest.permission.POST_NOTIFICATIONS)
        }
        setContent {
            MaterialTheme(
                colorScheme = darkColorScheme(
                    primary = Gold,
                    background = DeepBlue,
                    surface = DeepBlue,
                )
            ) {
                App()
            }
        }
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        handleTokenIntent(intent)
    }

    private fun handleTokenIntent(intent: Intent?) {
        intent?.getStringExtra("sync_token")?.let { SessionStore.token = it }
    }
}

@Composable
private fun App() {
    var tab by rememberSaveable { mutableIntStateOf(0) }
    var showTokenDialog by remember { mutableStateOf(false) }
    var ratingFor by remember { mutableStateOf<Session?>(null) }

    val lastId by SessionStore.lastSessionId.collectAsState()
    LaunchedEffect(lastId) {
        val id = lastId ?: return@LaunchedEffect
        SessionStore.sessions.value.find { it.id == id && it.completed }?.let { ratingFor = it }
    }

    Column(
        modifier = Modifier
            .fillMaxSize()
            .background(DeepBlue)
    ) {
        TabRow(
            selectedTabIndex = tab,
            containerColor = DeepBlue,
            contentColor = Gold,
        ) {
            Tab(selected = tab == 0, onClick = { tab = 0 }, text = { Text("Timer") })
            Tab(selected = tab == 1, onClick = { tab = 1 }, text = { Text("History") })
            Tab(selected = tab == 2, onClick = { tab = 2 }, text = { Text("Reading") })
        }
        when (tab) {
            0 -> TimerTab()
            1 -> HistoryTab(
                onEditSession = { ratingFor = it },
                onOpenToken = { showTokenDialog = true },
            )
            2 -> ReadingTab()
        }
    }

    if (showTokenDialog) {
        TokenDialog(onDismiss = { showTokenDialog = false })
    }
    ratingFor?.let { session ->
        RatingDialog(
            session = session,
            onSave = { rating, note ->
                SessionStore.updateRating(session.id, rating, note)
                SessionStore.lastSessionId.value = null
                ratingFor = null
            },
            onDismiss = {
                SessionStore.lastSessionId.value = null
                ratingFor = null
            },
        )
    }
}

// ---------------------------------------------------------------- Timer tab

@Composable
private fun TimerTab() {
    val context = LocalContext.current
    val timer by TimerService.state.collectAsState()
    var durationMin by rememberSaveable { mutableIntStateOf(20) }

    val phase = timer.phase
    val selectedTotalMs = durationMin * 60_000L

    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(horizontal = 32.dp),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center,
    ) {
        Text(
            text = when (phase) {
                Phase.Idle -> "Meditation"
                Phase.Running -> "Breathe"
                Phase.Paused -> "Paused"
                Phase.Done -> "Session complete"
            },
            color = Gold.copy(alpha = 0.8f),
            fontSize = 20.sp,
            letterSpacing = 3.sp,
        )

        Spacer(Modifier.height(32.dp))

        if (phase == Phase.Idle || phase == Phase.Done) {
            MinutePicker(durationMin = durationMin, onChange = { durationMin = it })
            Spacer(Modifier.height(32.dp))
            Button(
                onClick = { TimerService.start(context, selectedTotalMs) },
                colors = ButtonDefaults.buttonColors(containerColor = Gold, contentColor = DeepBlue),
            ) {
                Text(if (phase == Phase.Done) "Again" else "Begin", fontSize = 18.sp)
            }
        } else {
            val progress by animateFloatAsState(
                targetValue = (timer.remainingMs.toFloat() / timer.totalMs.coerceAtLeast(1L)).coerceIn(0f, 1f),
                animationSpec = tween(250),
                label = "progress",
            )
            Box(contentAlignment = Alignment.Center) {
                CircularProgressIndicator(
                    progress = { progress },
                    modifier = Modifier.size(260.dp),
                    color = Gold,
                    trackColor = Color.White.copy(alpha = 0.08f),
                    strokeWidth = 6.dp,
                )
                Text(
                    text = formatTime(timer.remainingMs),
                    color = Color.White,
                    fontSize = 56.sp,
                    fontWeight = FontWeight.Light,
                )
            }
            Spacer(Modifier.height(40.dp))
            Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
                if (phase == Phase.Running) {
                    OutlinedButton(onClick = { TimerService.send(context, TimerService.ACTION_PAUSE) }) {
                        Text("Pause", color = Gold)
                    }
                } else {
                    Button(
                        onClick = { TimerService.send(context, TimerService.ACTION_RESUME) },
                        colors = ButtonDefaults.buttonColors(containerColor = Gold, contentColor = DeepBlue),
                    ) { Text("Resume") }
                }
                OutlinedButton(onClick = { TimerService.send(context, TimerService.ACTION_STOP) }) {
                    Text("End", color = Color.White.copy(alpha = 0.6f))
                }
            }
        }
    }
}

private val PRESETS = listOf(20, 45, 60, 90)
private const val MAX_MINUTES = 180

@Composable
private fun MinutePicker(durationMin: Int, onChange: (Int) -> Unit) {
    val itemHeight = 44.dp
    val listState = rememberLazyListState(initialFirstVisibleItemIndex = durationMin - 1)
    val scope = rememberCoroutineScope()

    val centeredIndex by remember {
        derivedStateOf {
            val info = listState.layoutInfo
            if (info.visibleItemsInfo.isEmpty()) durationMin - 1
            else {
                val center = (info.viewportStartOffset + info.viewportEndOffset) / 2
                info.visibleItemsInfo.minByOrNull {
                    kotlin.math.abs((it.offset + it.size / 2) - center)
                }?.index ?: (durationMin - 1)
            }
        }
    }
    LaunchedEffect(Unit) {
        snapshotFlow { centeredIndex }.collect { onChange(it + 1) }
    }

    Box(contentAlignment = Alignment.Center) {
        Box(
            Modifier
                .width(150.dp)
                .height(itemHeight)
                .background(Gold.copy(alpha = 0.10f), RoundedCornerShape(12.dp))
        )
        Row(verticalAlignment = Alignment.CenterVertically) {
            LazyColumn(
                state = listState,
                flingBehavior = rememberSnapFlingBehavior(listState),
                modifier = Modifier
                    .height(itemHeight * 3)
                    .width(90.dp),
                contentPadding = PaddingValues(vertical = itemHeight),
                horizontalAlignment = Alignment.CenterHorizontally,
            ) {
                items((1..MAX_MINUTES).toList()) { min ->
                    val selected = min - 1 == centeredIndex
                    Box(Modifier.height(itemHeight), contentAlignment = Alignment.Center) {
                        Text(
                            text = "$min",
                            fontSize = if (selected) 32.sp else 20.sp,
                            fontWeight = if (selected) FontWeight.Medium else FontWeight.Light,
                            color = if (selected) Gold else Dim,
                        )
                    }
                }
            }
            Text("min", color = Dim, fontSize = 14.sp)
        }
    }

    Spacer(Modifier.height(24.dp))

    Row(horizontalArrangement = Arrangement.spacedBy(10.dp)) {
        PRESETS.forEach { min ->
            FilterChip(
                selected = durationMin == min,
                onClick = { scope.launch { listState.animateScrollToItem(min - 1) } },
                label = { Text("$min") },
                colors = FilterChipDefaults.filterChipColors(
                    selectedContainerColor = Gold,
                    selectedLabelColor = DeepBlue,
                    labelColor = Color.White.copy(alpha = 0.7f),
                ),
            )
        }
    }
}

// -------------------------------------------------------------- History tab

@Composable
private fun HistoryTab(onEditSession: (Session) -> Unit, onOpenToken: () -> Unit) {
    val sessions by SessionStore.sessions.collectAsState()
    val pending by SessionStore.pendingSync.collectAsState()

    Column(
        Modifier
            .fillMaxSize()
            .padding(horizontal = 20.dp)
    ) {
        Spacer(Modifier.height(20.dp))

        val totalMin = sessions.sumOf { it.actualMin }
        Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly) {
            Stat("${streak(sessions)}", "day streak")
            Stat("${sessions.size}", "sits")
            Stat("${totalMin / 60}h ${totalMin % 60}m", "total")
        }

        Spacer(Modifier.height(16.dp))

        Row(
            Modifier.fillMaxWidth(),
            horizontalArrangement = Arrangement.SpaceBetween,
            verticalAlignment = Alignment.CenterVertically,
        ) {
            Text(
                text = when {
                    SessionStore.token.isEmpty() -> "Sync not configured"
                    pending > 0 -> "$pending unsynced"
                    else -> "All synced"
                },
                color = if (pending > 0 || SessionStore.token.isEmpty()) Gold.copy(alpha = 0.8f) else Dim,
                fontSize = 12.sp,
            )
            Row {
                if (pending > 0 && SessionStore.token.isNotEmpty()) {
                    TextButton(onClick = { SessionStore.syncPending() }) {
                        Text("Sync now", color = Gold, fontSize = 12.sp)
                    }
                }
                TextButton(onClick = onOpenToken) {
                    Text("Token", color = Dim, fontSize = 12.sp)
                }
            }
        }

        HorizontalDivider(color = Color.White.copy(alpha = 0.08f))

        if (sessions.isEmpty()) {
            Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
                Text("No sits recorded yet", color = Dim)
            }
        } else {
            LazyColumn(Modifier.fillMaxSize()) {
                items(sessions, key = { it.id }) { s ->
                    SessionRow(s, onClick = { onEditSession(s) })
                    HorizontalDivider(color = Color.White.copy(alpha = 0.06f))
                }
            }
        }
    }
}

@Composable
private fun Stat(value: String, label: String) {
    Column(horizontalAlignment = Alignment.CenterHorizontally) {
        Text(value, color = Gold, fontSize = 24.sp, fontWeight = FontWeight.Medium)
        Text(label.uppercase(), color = Dim, fontSize = 10.sp, letterSpacing = 1.sp)
    }
}

@Composable
private fun SessionRow(s: Session, onClick: () -> Unit) {
    Column(
        Modifier
            .fillMaxWidth()
            .clickable(onClick = onClick)
            .padding(vertical = 12.dp, horizontal = 4.dp)
    ) {
        Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
            Text(formatDate(s.startedAt), color = Color.White.copy(alpha = 0.85f), fontSize = 14.sp)
            Text(
                text = if (s.rating != null) "●".repeat(s.rating) + "○".repeat(5 - s.rating) else "",
                color = Gold,
                fontSize = 12.sp,
                letterSpacing = 2.sp,
            )
        }
        Spacer(Modifier.height(2.dp))
        Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
            Text(
                text = "${s.actualMin} min" + if (s.completed) "" else " of ${s.plannedMin} (${s.completionPct}%)",
                color = if (s.completed) Gold else Color(0xFFF5C542),
                fontSize = 12.sp,
            )
            if (!s.synced) Text("unsynced", color = Dim, fontSize = 11.sp)
        }
        s.note?.let {
            Spacer(Modifier.height(2.dp))
            Text(it, color = Dim, fontSize = 12.sp, fontStyle = FontStyle.Italic)
        }
    }
}

// -------------------------------------------------------------- Reading tab

private data class Doc(val title: String, val subtitle: String, val asset: String)

private val DOCS = listOf(
    Doc("Pragmatic Jhanas Guide", "v4.3", "jhanas-guide.pdf"),
    Doc("Retreat Instructions", "v6.0.3 · Day 1 & 2", "retreat-instructions.pdf"),
)

@Composable
private fun ReadingTab() {
    val context = LocalContext.current
    Column(
        Modifier
            .fillMaxSize()
            .padding(20.dp)
    ) {
        DOCS.forEach { doc ->
            Column(
                Modifier
                    .fillMaxWidth()
                    .clickable { openPdf(context, doc) }
                    .background(Color.White.copy(alpha = 0.05f), RoundedCornerShape(12.dp))
                    .padding(20.dp)
            ) {
                Text(doc.title, color = Color.White.copy(alpha = 0.9f), fontSize = 16.sp)
                Spacer(Modifier.height(4.dp))
                Text(doc.subtitle, color = Dim, fontSize = 12.sp)
            }
            Spacer(Modifier.height(12.dp))
        }
        Text(
            "Opens in the system PDF viewer.",
            color = Dim,
            fontSize = 11.sp,
            textAlign = TextAlign.Center,
            modifier = Modifier.fillMaxWidth(),
        )
    }
}

private fun openPdf(context: Context, doc: Doc) {
    try {
        val dir = File(context.cacheDir, "reading").apply { mkdirs() }
        val outFile = File(dir, doc.asset)
        if (!outFile.exists() || outFile.length() == 0L) {
            context.assets.open("reading/${doc.asset}").use { input ->
                outFile.outputStream().use { input.copyTo(it) }
            }
        }
        val uri = FileProvider.getUriForFile(context, "${context.packageName}.files", outFile)
        context.startActivity(
            Intent(Intent.ACTION_VIEW)
                .setDataAndType(uri, "application/pdf")
                .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
        )
    } catch (e: Exception) {
        Toast.makeText(context, "Could not open PDF: ${e.message}", Toast.LENGTH_SHORT).show()
    }
}

// ----------------------------------------------------------------- Dialogs

@Composable
private fun RatingDialog(session: Session, onSave: (Int?, String?) -> Unit, onDismiss: () -> Unit) {
    var rating by remember { mutableStateOf(session.rating) }
    var note by remember { mutableStateOf(session.note ?: "") }

    AlertDialog(
        onDismissRequest = onDismiss,
        title = { Text("How deep was the sit?") },
        text = {
            Column {
                Row(
                    Modifier.fillMaxWidth(),
                    horizontalArrangement = Arrangement.Center,
                ) {
                    (1..5).forEach { i ->
                        Text(
                            text = if (rating != null && i <= rating!!) "●" else "○",
                            color = Gold,
                            fontSize = 34.sp,
                            modifier = Modifier
                                .clickable { rating = if (rating == i) null else i }
                                .padding(horizontal = 6.dp),
                        )
                    }
                }
                Spacer(Modifier.height(16.dp))
                OutlinedTextField(
                    value = note,
                    onValueChange = { note = it },
                    label = { Text("Note (optional)") },
                    modifier = Modifier.fillMaxWidth(),
                )
            }
        },
        confirmButton = {
            TextButton(onClick = { onSave(rating, note) }) { Text("Save", color = Gold) }
        },
        dismissButton = {
            TextButton(onClick = onDismiss) { Text("Skip", color = Dim) }
        },
    )
}

@Composable
private fun TokenDialog(onDismiss: () -> Unit) {
    var token by remember { mutableStateOf(SessionStore.token) }
    AlertDialog(
        onDismissRequest = onDismiss,
        title = { Text("Sync token") },
        text = {
            Column {
                Text(
                    "Bearer token for meditation.rehbock.xyz. Stored only on this device.",
                    fontSize = 12.sp,
                    color = Dim,
                )
                Spacer(Modifier.height(12.dp))
                OutlinedTextField(
                    value = token,
                    onValueChange = { token = it },
                    singleLine = true,
                    modifier = Modifier.fillMaxWidth(),
                )
            }
        },
        confirmButton = {
            TextButton(onClick = {
                SessionStore.token = token
                onDismiss()
            }) { Text("Save", color = Gold) }
        },
        dismissButton = {
            TextButton(onClick = onDismiss) { Text("Cancel", color = Dim) }
        },
    )
}

// ------------------------------------------------------------------ Helpers

private fun formatTime(ms: Long): String {
    val totalSec = (ms + 999) / 1000
    return "%d:%02d".format(totalSec / 60, totalSec % 60)
}

private val DATE_FMT = DateTimeFormatter.ofPattern("EEE d MMM · HH:mm")

private fun formatDate(iso: String): String = try {
    DATE_FMT.format(Instant.parse(iso).atZone(ZoneId.systemDefault()))
} catch (_: Exception) {
    iso.take(16).replace("T", " ")
}

private fun streak(sessions: List<Session>): Int {
    val days = sessions.mapNotNull {
        try {
            Instant.parse(it.startedAt).atZone(ZoneId.systemDefault()).toLocalDate()
        } catch (_: Exception) {
            null
        }
    }.toSet()
    var cursor = LocalDate.now()
    if (cursor !in days) cursor = cursor.minusDays(1)
    var n = 0
    while (cursor in days) {
        n++
        cursor = cursor.minusDays(1)
    }
    return n
}
