// Copyright (C) 2026 Jason M. Schwefel. All Rights Reserved.
package com.coldboreballisticsllc.scopedope.ui.screens.print

import android.Manifest
import android.app.Activity
import android.content.pm.PackageManager
import android.graphics.Bitmap
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import com.coldboreballisticsllc.scopedope.label.format.LabelShape
import com.coldboreballisticsllc.scopedope.label.renderer.ElevationSymbol
import com.coldboreballisticsllc.scopedope.label.renderer.RenderOptions
import androidx.core.content.ContextCompat
import androidx.hilt.navigation.compose.hiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import com.coldboreballisticsllc.scopedope.domain.model.LabelEntry
import com.coldboreballisticsllc.scopedope.domain.model.WindLabel
import com.coldboreballisticsllc.scopedope.domain.printer.PrinterState
import com.coldboreballisticsllc.scopedope.ui.components.ActionButton
import com.coldboreballisticsllc.scopedope.ui.session.AdjUnit
import com.coldboreballisticsllc.scopedope.ui.session.MeasureSystem
import com.coldboreballisticsllc.scopedope.ui.session.PrinterViewModel
import com.coldboreballisticsllc.scopedope.ui.session.SaveResult
import com.coldboreballisticsllc.scopedope.ui.session.SessionViewModel
import com.coldboreballisticsllc.scopedope.ui.theme.*

private val BLE_PERMISSIONS = arrayOf(
    Manifest.permission.BLUETOOTH_SCAN,
    Manifest.permission.BLUETOOTH_CONNECT,
)

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun PrintScreen(
    sessionVm: SessionViewModel = hiltViewModel(),
    printerVm: PrinterViewModel = hiltViewModel(),
) {
    val context          = LocalContext.current
    val state            by sessionVm.state.collectAsState()
    val printerUiState   by printerVm.uiState.collectAsState()
    val printerState     by printerVm.printerState.collectAsState()
    val saveResult       = printerUiState.saveResult
    val pendingReview    by printerVm.pendingReview.collectAsState()

    // Launch review dialog when the ViewModel signals readiness
    LaunchedEffect(pendingReview) {
        if (pendingReview != null) {
            (context as? Activity)?.let { printerVm.launchReview(it) }
        }
    }

    // ── Printer naming dialog state ───────────────────────────────────────────
    var showNameDialog  by remember { mutableStateOf(false) }
    var pendingPrint    by remember { mutableStateOf(false) }
    var renameTrigger   by remember { mutableStateOf(false) }

    // ── BLE permission request ────────────────────────────────────────────────
    var blePermDenied by remember { mutableStateOf(false) }
    val blePermLauncher = rememberLauncherForActivityResult(
        ActivityResultContracts.RequestMultiplePermissions()
    ) { grants ->
        if (grants.values.all { it }) printerVm.connect()
        else blePermDenied = true
    }
    val connectWithPermission: () -> Unit = {
        val allGranted = BLE_PERMISSIONS.all {
            ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
        }
        if (allGranted) printerVm.connect() else blePermLauncher.launch(BLE_PERMISSIONS)
    }

    // ── Label data ────────────────────────────────────────────────────────────
    val labelEntries = remember(state.results, state.labelFormat) {
        state.results
            .filter { it.includeOnLabel }
            .take(state.labelFormat.maxEntries)
            .map { LabelEntry(
                distanceYards = it.distanceYards,
                elevationMil  = it.elevationMil,
                windageMil    = it.windageMil,
                elevationMoa  = it.elevationMoa,
                windageMoa    = it.windageMoa,
            ) }
    }

    val windLabel = remember(state.wind) {
        if (state.wind.speedMph >= 0.5) WindLabel(state.wind.speedMph, state.wind.angleDeg)
        else null
    }

    val renderOptions = remember(state.adjUnit, printerUiState.printFullCircle, printerUiState.showGridlines,
                                   printerUiState.elevationSymbol, printerUiState.columnFillInterleaved,
                                   printerUiState.landscapeCol1Reversed,
                                   printerUiState.useSemanticColors, state.measureSystem) {
        RenderOptions(
            adjDecimals           = if (state.adjUnit == AdjUnit.MOA) 2 else 1,
            showGridlines         = printerUiState.showGridlines,
            fullCircle            = printerUiState.printFullCircle,
            elevationSymbol       = printerUiState.elevationSymbol,
            columnFillInterleaved = printerUiState.columnFillInterleaved,
            landscapeCol1Reversed = printerUiState.landscapeCol1Reversed,
            useMoa                = state.adjUnit == AdjUnit.MOA,
            useMetric             = state.measureSystem == MeasureSystem.METRIC,
            useSemanticColors     = printerUiState.useSemanticColors,
        )
    }
    val labelShape = state.labelFormat.shape
    val previewBitmap by produceState<Bitmap?>(null, labelEntries, windLabel, renderOptions, labelShape) {
        value = if (labelEntries.isEmpty()) null
        else withContext(Dispatchers.Default) {
            when (labelShape) {
                is LabelShape.Circle    -> printerVm.renderer.render(labelEntries, windLabel, 96, renderOptions)
                is LabelShape.Rectangle -> printerVm.rectRenderer.render(state.labelFormat, labelEntries, windLabel, 96, renderOptions)
            }
        }
    }

    val hasEntries       = labelEntries.isNotEmpty()
    val printerReady     = printerState == PrinterState.Ready
    val printerConnected = printerState !is PrinterState.Disconnected
                           && printerState !is PrinterState.Error
    val isPrinting       = printerState is PrinterState.Printing

    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text("Print / Export", style = MaterialTheme.typography.titleLarge) },
                colors = TopAppBarDefaults.topAppBarColors(
                    containerColor    = ColorBackground,
                    titleContentColor = ColorTextPrimary,
                ),
            )
        },
        containerColor = ColorBackground,
    ) { padding ->
        Column(
            modifier            = Modifier
                .padding(padding)
                .fillMaxSize()
                .verticalScroll(rememberScrollState())
                .padding(16.dp),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.spacedBy(16.dp),
        ) {
            Text(
                text  = state.labelFormat.displayName,
                style = MaterialTheme.typography.titleMedium,
                color = ColorTextMuted,
            )

            // ── Label preview ─────────────────────────────────────────────────
            val (previewW, previewH, previewClip) = when (val s = state.labelFormat.shape) {
                is LabelShape.Circle    -> Triple(240.dp, 240.dp, CircleShape)
                is LabelShape.Rectangle -> {
                    val ratio = s.widthMm / s.heightMm
                    if (ratio >= 1f) Triple(240.dp, (240 / ratio).dp, RectangleShape)
                    else             Triple((240 * ratio).dp, 240.dp, RectangleShape)
                }
            }
            val bitmap = previewBitmap
            if (bitmap != null) {
                Image(
                    bitmap             = bitmap.asImageBitmap(),
                    contentDescription = "Label preview",
                    modifier           = Modifier
                        .size(previewW, previewH)
                        .clip(previewClip)
                        .border(2.dp, ColorBorder, previewClip),
                )
            } else {
                Box(
                    modifier         = Modifier
                        .size(previewW, previewH)
                        .clip(previewClip)
                        .border(2.dp, ColorBorder, previewClip),
                    contentAlignment = Alignment.Center,
                ) {
                    Text(
                        text  = "No results to preview",
                        color = ColorTextMuted,
                        style = MaterialTheme.typography.bodyMedium,
                    )
                }
            }

            // ── Niimbot BLE ───────────────────────────────────────────────────
            val printerDisplayName = printerUiState.connectedPrinterName
                ?: printerUiState.connectedPrinterBtName
                ?: "Niimbot B1"

            NiimbotPanel(
                printerState       = printerState,
                printerDisplayName = printerDisplayName,
                isPrinting         = isPrinting,
                printerReady       = printerReady,
                printerConnected   = printerConnected,
                hasEntries         = hasEntries,
                onConnect          = connectWithPermission,
                onDisconnect       = printerVm::disconnect,
                onPrint            = {
                    // First print from this MAC with no stored name → prompt
                    if (printerUiState.connectedPrinterMac != null && printerUiState.connectedPrinterName == null) {
                        pendingPrint = true
                        showNameDialog = true
                    } else {
                        printerVm.printViaNiimbot(
                            labelEntries, windLabel, state.labelFormat,
                            state.adjUnit, state.measureSystem,
                            printerUiState.printFullCircle,
                        )
                    }
                },
                onRename           = if (printerConnected) {
                    { renameTrigger = true; showNameDialog = true }
                } else null,
            )

            // ── Printer naming dialog ─────────────────────────────────────────
            if (showNameDialog) {
                PrinterNameDialog(
                    currentName = if (renameTrigger) printerDisplayName else "",
                    btName      = printerUiState.connectedPrinterBtName ?: "",
                    onSave      = { name ->
                        printerVm.savePrinterName(name)
                        showNameDialog  = false
                        renameTrigger   = false
                        if (pendingPrint) {
                            pendingPrint = false
                            printerVm.printViaNiimbot(
                                labelEntries, windLabel, state.labelFormat,
                                state.adjUnit, state.measureSystem,
                                printerUiState.printFullCircle,
                            )
                        }
                    },
                    onDismiss   = {
                        showNameDialog = false
                        pendingPrint   = false
                        renameTrigger  = false
                    },
                )
            }

            if (blePermDenied) {
                Text(
                    text  = "Bluetooth permission required to connect",
                    color = ColorError,
                    style = MaterialTheme.typography.bodyMedium,
                )
                LaunchedEffect(blePermDenied) {
                    delay(4_000)
                    blePermDenied = false
                }
            }

            HorizontalDivider(color = ColorBorder)

            // ── PNG export ────────────────────────────────────────────────────
            OutlinedButton(
                onClick  = {
                    printerVm.saveLabelAsPng(
                        labelEntries, windLabel, state.labelFormat,
                        state.adjUnit, state.measureSystem,
                        state.ammo?.caliber ?: "unknown",
                    )
                },
                enabled  = hasEntries,
                modifier = Modifier.fillMaxWidth(),
                colors   = ButtonDefaults.outlinedButtonColors(contentColor = ColorAmber),
                border   = BorderStroke(1.dp, ColorBorder),
            ) { Text("Save PNG to Gallery") }

            // ── Save result feedback ──────────────────────────────────────────
            when (val r = saveResult) {
                is SaveResult.Success -> {
                    Text(
                        text  = "Saved to Pictures/ScopeDOPE",
                        color = ColorSuccess,
                        style = MaterialTheme.typography.bodyMedium,
                    )
                    LaunchedEffect(r) {
                        delay(3_000)
                        printerVm.clearSaveResult()
                    }
                }
                is SaveResult.Failure -> {
                    Text(
                        text  = "Save failed: ${r.message}",
                        color = ColorError,
                        style = MaterialTheme.typography.bodyMedium,
                    )
                    LaunchedEffect(r) {
                        delay(4_000)
                        printerVm.clearSaveResult()
                    }
                }
                null -> {}
            }
        }
    }
}

// ── Niimbot connection panel ──────────────────────────────────────────────────

@Composable
private fun NiimbotPanel(
    printerState:      PrinterState,
    printerDisplayName: String,
    isPrinting:        Boolean,
    printerReady:      Boolean,
    printerConnected:  Boolean,
    hasEntries:        Boolean,
    onConnect:         () -> Unit,
    onDisconnect:      () -> Unit,
    onPrint:           () -> Unit,
    onRename:          (() -> Unit)?,
) {
    Column(
        modifier            = Modifier.fillMaxWidth(),
        verticalArrangement = Arrangement.spacedBy(8.dp),
    ) {
        // Status indicator
        Row(
            modifier              = Modifier.fillMaxWidth(),
            horizontalArrangement = Arrangement.SpaceBetween,
            verticalAlignment     = Alignment.CenterVertically,
        ) {
            Column {
                Text(
                    text  = printerDisplayName,
                    style = MaterialTheme.typography.titleMedium,
                    color = ColorTextPrimary,
                )
                if (onRename != null) {
                    Text(
                        text  = "Rename",
                        style = MaterialTheme.typography.labelSmall,
                        color = ColorAmberDim,
                        modifier = Modifier.clickable(onClick = onRename),
                    )
                }
            }
            Text(
                text  = printerStatusText(printerState),
                style = MaterialTheme.typography.labelMedium,
                color = printerStatusColor(printerState),
            )
        }

        // Print progress bar
        if (isPrinting) {
            val progress = (printerState as PrinterState.Printing).progress
            LinearProgressIndicator(
                progress     = { progress },
                modifier     = Modifier.fillMaxWidth(),
                color        = ColorAmber,
                trackColor   = ColorSurface2,
            )
        }

        // Connect / Disconnect / Print buttons
        Row(
            modifier              = Modifier.fillMaxWidth(),
            horizontalArrangement = Arrangement.spacedBy(8.dp),
        ) {
            if (!printerConnected) {
                ActionButton(
                    text     = "Connect",
                    onClick  = onConnect,
                    modifier = Modifier.weight(1f),
                )
            } else {
                OutlinedButton(
                    onClick  = onDisconnect,
                    modifier = Modifier.weight(1f),
                    colors   = ButtonDefaults.outlinedButtonColors(contentColor = ColorError),
                    border   = BorderStroke(1.dp, ColorError),
                ) { Text("Disconnect") }
            }

            ActionButton(
                text     = if (isPrinting) "Printing…" else "Print",
                enabled  = printerReady && hasEntries && !isPrinting,
                onClick  = onPrint,
                modifier = Modifier.weight(1f),
            )
        }
    }
}

// ── Printer naming dialog ─────────────────────────────────────────────────────

/**
 * Shown on the first print from a new printer MAC, and when the user taps Rename.
 * [currentName] is pre-filled when renaming; blank on first-name.
 * [btName] is the BLE-advertised device name shown as a hint / quick-fill option.
 */
@Composable
private fun PrinterNameDialog(
    currentName: String,
    btName:      String,
    onSave:      (String) -> Unit,
    onDismiss:   () -> Unit,
) {
    var text by remember(currentName) {
        mutableStateOf(TextFieldValue(currentName, androidx.compose.ui.text.TextRange(currentName.length)))
    }

    AlertDialog(
        onDismissRequest = onDismiss,
        containerColor   = ColorSurface,
        title = {
            Text(
                text  = if (currentName.isEmpty()) "Name this printer" else "Rename printer",
                color = ColorTextPrimary,
            )
        },
        text = {
            Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
                if (currentName.isEmpty()) {
                    Text(
                        text  = "Give this printer a friendly name so you can recognise it at a match.",
                        style = MaterialTheme.typography.bodyMedium,
                        color = ColorTextMuted,
                    )
                }
                OutlinedTextField(
                    value         = text,
                    onValueChange = { text = it },
                    label         = { Text("Printer name") },
                    placeholder   = { Text(btName.ifEmpty { "e.g. My B1" }, color = ColorTextMuted) },
                    singleLine    = true,
                    colors        = OutlinedTextFieldDefaults.colors(
                        focusedBorderColor   = ColorAmber,
                        focusedLabelColor    = ColorAmber,
                        unfocusedBorderColor = ColorBorder,
                        unfocusedLabelColor  = ColorTextMuted,
                        cursorColor          = ColorAmber,
                    ),
                )
                if (btName.isNotEmpty() && text.text != btName) {
                    Text(
                        text  = "Use device name: $btName",
                        style = MaterialTheme.typography.labelSmall,
                        color = ColorAmberDim,
                        modifier = Modifier.clickable { text = TextFieldValue(btName, androidx.compose.ui.text.TextRange(btName.length)) },
                    )
                }
            }
        },
        confirmButton = {
            TextButton(
                onClick = { onSave(text.text.trim().ifEmpty { btName }) },
                colors  = ButtonDefaults.textButtonColors(contentColor = ColorAmber),
            ) { Text("Save") }
        },
        dismissButton = {
            TextButton(
                onClick = onDismiss,
                colors  = ButtonDefaults.textButtonColors(contentColor = ColorTextMuted),
            ) { Text("Cancel") }
        },
    )
}

private fun printerStatusText(state: PrinterState): String = when (state) {
    PrinterState.Disconnected -> "Disconnected"
    PrinterState.Scanning     -> "Scanning…"
    PrinterState.Connecting   -> "Connecting…"
    PrinterState.Ready        -> "Ready"
    is PrinterState.Printing  -> if (state.progress < 0.99f) "Sending to printer… ${(state.progress * 100).toInt()}%" else "Printing"
    PrinterState.Done         -> "Done"
    is PrinterState.Error     -> "Error: ${state.message}"
}

private fun printerStatusColor(state: PrinterState) = when (state) {
    PrinterState.Ready        -> ColorSuccess
    PrinterState.Done         -> ColorSuccess
    is PrinterState.Printing  -> ColorAmber
    is PrinterState.Error     -> ColorError
    else                      -> ColorTextMuted
}
