package com.github.premnirmal.ticker.widget

import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
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.tooling.preview.AndroidUiModes.UI_MODE_NIGHT_YES
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.TextUnitType
import androidx.compose.ui.unit.dp
import com.github.premnirmal.ticker.network.data.Holding
import com.github.premnirmal.ticker.network.data.Position
import com.github.premnirmal.ticker.network.data.Quote
import com.github.premnirmal.tickerwidget.R
import kotlin.random.Random

@Composable
fun GlanceWidgetPreview(
    modifier: Modifier,
    widgetData: SerializableWidgetState,
    quotes: List<Quote>,
    onRefreshClick: () -> Unit = {},
) {
    val columns = when {
        widgetData.sizePref > 0 -> widgetData.sizePref
        widgetData.layoutType == SerializableLayoutType.MyPortfolio -> 1
        else -> 2
    }
    Box(
        modifier = modifier
            .fillMaxWidth()
            .toBackgroundPainter(widgetData)
            // .paint(painterResource(widgetData.backgroundResource))
            .padding(6.dp)
    ) {
        Column(
            modifier = Modifier.fillMaxWidth()
        ) {
            if (quotes.isEmpty()) {
                Box(
                    modifier = Modifier.fillMaxWidth(),
                    contentAlignment = Alignment.Center
                ) {
                    Text(
                        text = stringResource(R.string.no_symbols),
                        style = TextStyle(
                            color = colorResource(R.color.text_widget_negative),
                            fontSize = TextUnit(16f, TextUnitType.Sp),
                            textAlign = TextAlign.Center,
                            fontWeight = FontWeight.Bold,
                        ),
                    )
                }
            } else {
                if (!widgetData.hideWidgetHeader) {
                    Header(widgetData, onRefreshClick)
                }
                QuotesGrid(columns, widgetData, quotes)
            }
        }
    }
}

@Composable
private fun Header(
    widgetData: SerializableWidgetState,
    onRefreshClick: () -> Unit,
) {
    val lastUpdatedText = when (widgetData.fetchState) {
        is SerializableFetchState.Success -> stringResource(R.string.last_fetch, widgetData.fetchState.displayString)
        is SerializableFetchState.Failure -> stringResource(R.string.refresh_failed)
        else -> SerializableFetchState.NotFetched.displayString
    }
    Row(
        modifier = Modifier.fillMaxWidth(),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        val fontSize = widgetData.fontSize
        Text(
            modifier = Modifier
                .weight(1f)
                .padding(horizontal = 2.dp),
            text = lastUpdatedText,
            style = TextStyle(
                color = colorResource(R.color.text_widget_header),
                fontSize = TextUnit(fontSize, TextUnitType.Sp),
                textAlign = TextAlign.Start,
                fontWeight = FontWeight.Normal,
            ),
        )

        if (widgetData.showRefreshButton) {
            Box(
                modifier = Modifier
                    .wrapContentSize()
                    .clickable(onClick = onRefreshClick),
                contentAlignment = Alignment.Center,
            ) {
                if (widgetData.isRefreshing) {
                    CircularProgressIndicator(
                        modifier = Modifier.size(18.dp),
                        color = colorResource(R.color.text_widget_header),
                    )
                } else {
                    Image(
                        modifier = Modifier.size(18.dp),
                        painter = painterResource(R.drawable.ic_refresh),
                        contentDescription = null,
                        colorFilter = ColorFilter.tint(colorResource(R.color.text_widget_header)),
                    )
                }
            }
        }
    }
}

@Composable
private fun QuotesGrid(
    columns: Int,
    widgetData: SerializableWidgetState,
    quotes: List<Quote>,
) {
    var changeType by remember(widgetData) { mutableStateOf(widgetData.changeType) }
    val textColor = colorResource(widgetData.textColor)
    val fontSize = widgetData.fontSize
    val layoutType = remember(widgetData) { widgetData.layoutType }
    val isBold = widgetData.boldText
    LazyVerticalGrid(
        modifier = Modifier,
        columns = GridCells.Fixed(columns),
    ) {
        items(items = quotes, key = { it.symbol }) { stock ->
            val changeValueFormatted = stock.changeString()
            val changePercentFormatted = stock.changePercentString()
            val priceFormatted = remember(widgetData.showCurrency) {
                if (widgetData.showCurrency) {
                    stock.priceFormat.format(stock.lastTradePrice)
                } else {
                    stock.priceString()
                }
            }
            val displayName = stock.properties?.displayname.takeUnless { it.isNullOrBlank() } ?: stock.symbol
            val change = stock.change
            val changeInPercent = stock.changeInPercent
            val widgetChangeColor = remember(change, changeInPercent) {
                widgetData.getChangeColor(change, changeInPercent)
            }
            val changeColor = colorResource(widgetChangeColor)
            val changeFormatted = remember(changeType, layoutType) {
                if (layoutType == SerializableLayoutType.Tabs) {
                    changeValueFormatted
                } else {
                    when (changeType) {
                        SerializableChangeType.Value -> changeValueFormatted
                        SerializableChangeType.Percent -> changePercentFormatted
                    }
                }
            }

            val onClickValue = {
                if (layoutType == SerializableLayoutType.Fixed) {
                    changeType = when (changeType) {
                        SerializableChangeType.Value -> SerializableChangeType.Percent
                        SerializableChangeType.Percent -> SerializableChangeType.Value
                    }
                }
            }

            if (layoutType == SerializableLayoutType.MyPortfolio) {
                MyPortfolio(
                    stock = stock,
                    widgetData = widgetData,
                )
            } else {
                Row(
                    modifier = Modifier.fillMaxWidth(),
                    verticalAlignment = Alignment.CenterVertically,
                ) {
                    Text(
                        modifier = Modifier
                            .weight(1f)
                            .padding(horizontal = 2.dp),
                        text = displayName,
                        style = TextStyle(
                            color = textColor,
                            fontSize = TextUnit(fontSize, TextUnitType.Sp),
                            textAlign = TextAlign.Start,
                            fontWeight = FontWeight.Medium,
                        ),
                        maxLines = 1,
                    )
                    Text(
                        modifier = Modifier.padding(horizontal = 2.dp),
                        text = priceFormatted,
                        style = TextStyle(
                            color = textColor,
                            fontSize = TextUnit(fontSize, TextUnitType.Sp),
                            fontWeight = FontWeight.Normal,
                            textAlign = TextAlign.End,
                        ),
                        maxLines = 1,
                    )

                    Text(
                        modifier = Modifier
                            .weight(1f)
                            .padding(end = 2.dp)
                            .clickable(onClick = onClickValue),
                        text = changeFormatted,
                        style = TextStyle(
                            color = changeColor,
                            fontSize = TextUnit(fontSize, TextUnitType.Sp),
                            fontWeight = if (isBold) FontWeight.Bold else FontWeight.Normal,
                            textAlign = TextAlign.End,
                        ),
                        maxLines = 1,
                    )

                    if (layoutType == SerializableLayoutType.Tabs) {
                        Text(
                            modifier = Modifier
                                .weight(1f)
                                .padding(end = 2.dp),
                            text = changePercentFormatted,
                            style = TextStyle(
                                color = changeColor,
                                fontSize = TextUnit(fontSize, TextUnitType.Sp),
                                fontWeight = if (isBold) FontWeight.Bold else FontWeight.Normal,
                                textAlign = TextAlign.End,
                            ),
                            maxLines = 1,
                        )
                    }
                }
            }
        }
    }
}

@Composable
private fun MyPortfolio(
    stock: Quote,
    widgetData: SerializableWidgetState,
) {
    val textColor = colorResource(widgetData.textColor)
    val fontSize = widgetData.fontSize
    val gainLossFormatted = stock.gainLossString()
    val gainLossPercentFormatted = stock.gainLossPercentString()
    val priceFormatted = if (widgetData.showCurrency) {
        stock.priceFormat.format(stock.lastTradePrice)
    } else {
        stock.priceString()
    }
    val holdingsFormatted = if (widgetData.showCurrency) {
        stock.priceFormat.format(stock.holdings())
    } else {
        stock.holdingsString()
    }
    val displayName = stock.properties?.displayname.takeUnless { it.isNullOrBlank() } ?: stock.symbol
    val gainLoss = stock.gainLoss()
    val gainLossColor = colorResource(widgetData.getChangeColor(gainLoss, gainLoss))
    Column(
        modifier = Modifier.fillMaxWidth()
    ) {
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .wrapContentHeight()
        ) {
            Text(
                modifier = Modifier
                    .weight(1f)
                    .padding(end = 2.dp),
                text = displayName,
                style = TextStyle(
                    color = textColor,
                    fontSize = TextUnit(fontSize, TextUnitType.Sp),
                    textAlign = TextAlign.Start,
                    fontWeight = FontWeight.Medium,
                ),
                maxLines = 1,
            )
            Text(
                modifier = Modifier.padding(end = 2.dp),
                text = holdingsFormatted,
                style = TextStyle(
                    color = textColor,
                    fontSize = TextUnit(fontSize, TextUnitType.Sp),
                    textAlign = TextAlign.End,
                    fontWeight = FontWeight.Normal,
                ),
                maxLines = 1,
            )
            Text(
                modifier = Modifier
                    .weight(1f)
                    .padding(end = 2.dp),
                text = gainLossFormatted,
                style = TextStyle(
                    color = gainLossColor,
                    fontSize = TextUnit(fontSize, TextUnitType.Sp),
                    textAlign = TextAlign.End,
                    fontWeight = FontWeight.Normal,
                ),
                maxLines = 1,
            )
            Text(
                modifier = Modifier
                    .weight(1f)
                    .padding(end = 2.dp),
                text = gainLossPercentFormatted,
                style = TextStyle(
                    color = gainLossColor,
                    fontSize = TextUnit(fontSize, TextUnitType.Sp),
                    textAlign = TextAlign.End,
                    fontWeight = FontWeight.Normal,
                ),
                maxLines = 1,
            )
        }

        Row(
            modifier = Modifier
                .fillMaxWidth()
                .wrapContentHeight()
        ) {
            Text(
                modifier = Modifier.weight(1f),
                text = priceFormatted,
                style = TextStyle(
                    color = textColor,
                    fontSize = TextUnit(fontSize, TextUnitType.Sp),
                    fontWeight = FontWeight.Normal,
                    textAlign = TextAlign.Start,
                    fontStyle = FontStyle.Italic,
                ),
                maxLines = 1,
            )
        }
    }
}

@Composable
private fun Modifier.toBackgroundPainter(widgetData: SerializableWidgetState): Modifier {
    return when (widgetData.backgroundResource) {
        R.drawable.transparent_widget_bg -> { this }
        R.drawable.translucent_widget_bg -> {
            if (widgetData.isDarkMode) {
                this.background(
                    color = colorResource(R.color.translucent_dark),
                    shape = RoundedCornerShape(12.dp),
                )
            } else {
                this.background(
                    color = colorResource(R.color.translucent),
                    shape = RoundedCornerShape(12.dp),
                )
            }
        }
        else -> {
            if (widgetData.isDarkMode) {
                this.background(
                    color = colorResource(R.color.widget_bg_dark),
                    shape = RoundedCornerShape(12.dp),
                )
            } else {
                this.background(
                    color = colorResource(R.color.widget_bg),
                    shape = RoundedCornerShape(12.dp),
                )
            }
        }
    }
}

@Composable
@Preview(uiMode = UI_MODE_NIGHT_YES)
private fun PreviewDark() {
    Box(
        modifier = Modifier
            .background(color = MaterialTheme.colorScheme.inverseSurface)
            .padding(20.dp)
    ) {
        val data = previewDataState()
        GlanceWidgetPreview(
            modifier = Modifier.width(300.dp),
            quotes = listOf(
                fakeQuote("AAPL", fakePosition("AAPL")),
                fakeQuote("MSFT", fakePosition("MSFT")),
                fakeQuote("GOOG", fakePosition("GOOG")),
                fakeQuote("AMZN", fakePosition("AMZN")),
                fakeQuote("BRK-B", fakePosition("BRK-B"))
            ),
            widgetData = data,
        )
    }
}

@Composable
@Preview
private fun Preview() {
    Box(
        modifier = Modifier
            .background(color = MaterialTheme.colorScheme.inverseSurface)
            .padding(20.dp)
    ) {
        val data = previewDataState()
        GlanceWidgetPreview(
            modifier = Modifier.width(300.dp),
            quotes = listOf(
                fakeQuote("AAPL", fakePosition("AAPL")),
                fakeQuote("MSFT", fakePosition("MSFT")),
                fakeQuote("GOOG", fakePosition("GOOG")),
                fakeQuote("AMZN", fakePosition("AMZN")),
                fakeQuote("BRK-B", fakePosition("BRK-B"))
            ),
            widgetData = data,
        )
    }
}

private fun fakeQuote(
    symbol: String,
    position: Position? = null
): Quote {
    return Quote(
        symbol = symbol,
        position = position,
        lastTradePrice = Random.nextDouble(122434.4242).toFloat(),
        change = Random.nextDouble(48.0).toFloat(),
        changeInPercent = Random.nextDouble(12.0).toFloat(),
    )
}

private fun fakePosition(symbol: String): Position {
    return Position(
        symbol = symbol,
        holdings = mutableListOf(
            Holding(
                symbol = symbol,
                shares = Random.nextDouble(10.0).toFloat(),
                price = Random.nextDouble(1434.4242).toFloat(),
            )
        )
    )
}

private fun previewDataState(
    layoutType: IWidgetData.LayoutType = IWidgetData.LayoutType.Fixed,
): SerializableWidgetState = SerializableWidgetState(
    layoutType = SerializableLayoutType.from(layoutType),
    showCurrency = false,
    boldText = false,
    changeType = SerializableChangeType.Percent,
    sizePref = 0,
    fontSize = 12f,
    isDarkMode = false,
    hideWidgetHeader = false,
    negativeTextColor = R.color.text_widget_negative,
    positiveTextColor = R.color.text_widget_positive,
    textColor = R.color.widget_text,
    backgroundResource = R.drawable.app_widget_background,
    isRefreshing = false,
    fetchState = SerializableFetchState.Success(System.currentTimeMillis()),
)
