/*
 * Geekttrss is a RSS feed reader application on the Android Platform.
 *
 * Copyright (C) 2017-2024 by Frederic-Charles Barthelery.
 *
 * This file is part of Geekttrss.
 *
 * Geekttrss is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Geekttrss is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Geekttrss.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.geekorum.ttrss.manage_feeds

import android.accounts.Account
import android.app.Application
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.automirrored.filled.AddToHomeScreen
import androidx.compose.material.icons.filled.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.graphics.drawable.IconCompat
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewModelScope
import androidx.work.Constraints
import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import coil.compose.rememberAsyncImagePainter
import coil.imageLoader
import coil.request.ImageRequest
import com.geekorum.ttrss.articles_list.ArticleListActivity
import com.geekorum.ttrss.articles_list.createFeedDeepLink
import com.geekorum.ttrss.data.Feed
import com.geekorum.ttrss.data.ManageFeedsDao
import com.geekorum.ttrss.data.feedsettings.FeedSettings
import com.geekorum.ttrss.data.feedsettings.FeedSettingsRepository
import com.geekorum.ttrss.data.feedsettings.copy
import com.geekorum.ttrss.manage_feeds.workers.UnsubscribeWorker
import com.geekorum.ttrss.ui.AppTheme3
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
import com.geekorum.ttrss.R as appR

private const val ARG_FEED_ID = "feedId"

/**
 * ViewModel to edit preferences for a feed
 */
@OptIn(ExperimentalCoroutinesApi::class)
@HiltViewModel
class EditFeedViewModel @Inject constructor(
    private val application: Application,
    private val savedStateHandle: SavedStateHandle,
    private val account: Account,
    private val feedsDao: ManageFeedsDao,
    private val feedSettingsRepository: FeedSettingsRepository,
): ViewModel() {

    private val canCreatePinShortcut = ShortcutManagerCompat.isRequestPinShortcutSupported(application)

    private val feed = savedStateHandle.getStateFlow<Long?>(ARG_FEED_ID, null)
        .filterNotNull()
        .flatMapLatest {
        feedsDao.getFeedById(it)
    }

    private val feedSettings = savedStateHandle.getStateFlow<Long?>(ARG_FEED_ID, null)
        .filterNotNull()
        .flatMapLatest {
            feedSettingsRepository.getFeedSettings(it)
        }

    val uiState = feed.combine(feedSettings) { feedInfo, settings ->
        EditFeedUiState(feed = feedInfo?.feed,
            faviconUrl = feedInfo?.favIcon?.url,
            canCreatePinShortcut = canCreatePinShortcut,
            isSyncingAutomatically = settings?.syncPeriodically ?: true
        )
    }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), EditFeedUiState())

    fun unsubscribeFeed() {
        val currentUiState = uiState.value
        val feed = checkNotNull(currentUiState.feed)
        unsubscribeFeed(feed.id)
    }

    private fun unsubscribeFeed(feedId: Long) {
        viewModelScope.launch {
            feedsDao.updateIsSubscribedFeed(feedId, false)
        }
        val workManager = WorkManager.getInstance(application)
        val constraints = Constraints.Builder()
            .setRequiredNetworkType(NetworkType.CONNECTED)
            .build()
        val request = OneTimeWorkRequestBuilder<UnsubscribeWorker>()
            .setConstraints(constraints)
            .setInputData(UnsubscribeWorker.getInputData(account, feedId))
            .build()
        workManager.enqueue(request)
    }

    fun createShortcut(context: Context) = viewModelScope.launch {
        val feed = checkNotNull(uiState.value.feed)
        val feedIconUrl = uiState.value.faviconUrl

        val shortcutTitle = feed.displayTitle.takeIf { it.isNotBlank() } ?: feed.title
        val intent = Intent(context, ArticleListActivity::class.java).apply {
            data = createFeedDeepLink(feed, shortcutTitle)
            action = Intent.ACTION_VIEW
        }

        val feedIconBitmap = try {
            getFeedIconBitmap(context, feedIconUrl)
        } catch (e: Exception) {
            Timber.w(e, "Unable to get favicon bitmap")
            null
        }

        val shortcutIcon = feedIconBitmap?.let { IconCompat.createWithBitmap(it) }
            ?: IconCompat.createWithResource(context, appR.mipmap.ic_launcher)

        val shortcutInfo = ShortcutInfoCompat.Builder(context, getShortcutId(feed.id))
            .setShortLabel(shortcutTitle)
            .setLongLabel(feed.displayTitle.takeIf { it.isNotBlank() } ?: feed.title)
            .setIcon(shortcutIcon)
            .setIntent(intent)
            .build()
        ShortcutManagerCompat.requestPinShortcut(context, shortcutInfo, null)
    }

    fun setSyncAutomatically(value: Boolean) = viewModelScope.launch {
        val feedId: Long = savedStateHandle[ARG_FEED_ID]!!
        val feedSettings = feedSettingsRepository.getFeedSettings(feedId).firstOrNull() ?: FeedSettings.getDefaultInstance()
        val update = feedSettings.copy { syncPeriodically = value }
        feedSettingsRepository.updateFeedSettings(feedId, update)
    }

    private suspend fun getFeedIconBitmap(
        context: Context,
        feedIconUrl: String?
    ): Bitmap? {
        val imageLoader = context.imageLoader
        val request = ImageRequest.Builder(context)
            .data(feedIconUrl)
            .build()
        val drawable = imageLoader.execute(request).drawable
        val bitmap = (drawable as? BitmapDrawable)?.bitmap
        return bitmap
    }

    private fun getShortcutId(feedId: Long) = "FEED_$feedId"
}

@HiltViewModel(assistedFactory = EditSpecialFeedViewModel.Factory::class)
class EditSpecialFeedViewModel @AssistedInject constructor(
    @Assisted val feedId: Long,
    private val application: Application,
): ViewModel() {

    @AssistedFactory
    interface Factory {
        fun create(feedId: Long): EditSpecialFeedViewModel
    }

    val uiState = flow {
        val canCreatePinShortcut = ShortcutManagerCompat.isRequestPinShortcutSupported(application)
        val feed = Feed.createVirtualFeedForId(feedId)
        val state = EditFeedUiState(feed = feed, canCreatePinShortcut = canCreatePinShortcut)
        emit(state)
    }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), EditFeedUiState())

    fun createShortcut(context: Context, title: String) = viewModelScope.launch {
        val feed = checkNotNull(uiState.value.feed)

        val intent = Intent(context, ArticleListActivity::class.java).apply {
            data = createFeedDeepLink(feed, title)
            action = Intent.ACTION_VIEW
        }

        val shortcutIcon = IconCompat.createWithResource(context, appR.mipmap.ic_launcher)

        val shortcutInfo = ShortcutInfoCompat.Builder(context, getShortcutId(feed.id))
            .setShortLabel(title)
            .setLongLabel(title)
            .setIcon(shortcutIcon)
            .setIntent(intent)
            .build()
        ShortcutManagerCompat.requestPinShortcut(context, shortcutInfo, null)
    }

    private fun getShortcutId(feedId: Long) = "FEED_$feedId"
}



data class EditFeedUiState(
    val feed: Feed? = null,
    val faviconUrl: String? = null,
    val canCreatePinShortcut: Boolean = false,
    val isSyncingAutomatically: Boolean = true,
) {
    val title: String = feed?.title ?: ""
    val url: String = feed?.url ?: ""
    val isSubscribed: Boolean = feed?.isSubscribed ?: true
}


@Composable
fun EditFeedScreen(feedId: Long, navigateBack: () -> Unit) {
    if (Feed.isVirtualFeed(feedId)) {
        EditSpecialFeedScreen(feedId)
    } else {
        EditFeedScreen(navigateBack = navigateBack)
    }
}

@Composable
fun EditFeedScreen(viewModel: EditFeedViewModel = dfmHiltViewModel(), navigateBack: () -> Unit) {
    val uiState by viewModel.uiState.collectAsStateWithLifecycle()
    LaunchedEffect(uiState.isSubscribed) {
        if (!uiState.isSubscribed) {
            navigateBack()
        }
    }
    val context = LocalContext.current
    EditFeedScreen(uiState = uiState,
        onUnsubscribeFeed = viewModel::unsubscribeFeed,
        onCreateShortcutToFeed = {
            viewModel.createShortcut(context)
        },
        onSyncAutomaticallyChange = {
            viewModel.setSyncAutomatically(it)
        })
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun EditFeedScreen(
    uiState: EditFeedUiState,
    onUnsubscribeFeed: () -> Unit,
    onCreateShortcutToFeed: () -> Unit,
    onSyncAutomaticallyChange: (Boolean) -> Unit,
) {
    Scaffold(topBar = {
        LargeTopAppBar(
            title = {
                Row(verticalAlignment = Alignment.CenterVertically) {
                    val feedIconPainter = rememberAsyncImagePainter(
                        model = uiState.faviconUrl,
                        placeholder = painterResource(appR.drawable.ic_rss_feed_orange),
                        fallback = painterResource(appR.drawable.ic_rss_feed_orange),
                        error = painterResource(appR.drawable.ic_rss_feed_orange),
                    )
                    Image(
                        painter = feedIconPainter,
                        contentDescription = null,
                        contentScale = ContentScale.FillBounds,
                        modifier = Modifier
                            .padding(end = 16.dp)
                            .size(48.dp)
                    )
                    Text(uiState.title,
                        overflow = TextOverflow.Ellipsis,
                        maxLines = 2,
                    )
                }
            },
            colors = TopAppBarDefaults.topAppBarColors(
                containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp)
            ))
    }) {
        var showUnsubscribeDialog by remember { mutableStateOf(false) }

        Column(
            Modifier
                .padding(it)
                .fillMaxWidth()
        ) {
            Column(Modifier.padding(16.dp)) {
                Text(stringResource(R.string.lbl_feed_url),
                    style = MaterialTheme.typography.titleMedium,
                    fontWeight = FontWeight.Bold)
                Text(uiState.url)
            }

            FeedsSettings(syncAutomatically = uiState.isSyncingAutomatically,
                onSyncAutomaticallyChange = onSyncAutomaticallyChange,
                modifier = Modifier.padding(bottom = 16.dp))

            Column(
                Modifier
                    .padding(horizontal = 16.dp)
                    .align(Alignment.End)
            ) {
                if (uiState.canCreatePinShortcut) {
                    CreateShortcutButton(onClick = onCreateShortcutToFeed,
                        modifier = Modifier.align(Alignment.End))
                }

                TextButton(modifier = Modifier
                    .padding(top = 16.dp)
                    .align(Alignment.End),
                    onClick = { showUnsubscribeDialog = true }) {
                    Text(stringResource(R.string.btn_unsubscribe))
                }
            }
        }

        if (showUnsubscribeDialog) {
            ConfirmUnsubscribeDialog(
                feed = uiState.feed!!,
                onDismissRequest = { showUnsubscribeDialog = false },
                onConfirmClick = onUnsubscribeFeed
            )
        }
    }
}

@Composable
private fun FeedsSettings(
    syncAutomatically: Boolean,
    onSyncAutomaticallyChange: (Boolean) -> Unit,
    modifier: Modifier = Modifier
) {
    Column(modifier) {
        SwitchPreference(
            headlineContent = {
                Text("Sync automatically")
            },
            checked = syncAutomatically,
            onCheckedChange = onSyncAutomaticallyChange
        )
    }
}

@Composable
private fun SwitchPreference(
    checked: Boolean,
    onCheckedChange: (Boolean) -> Unit,
    headlineContent: @Composable () -> Unit,
    modifier: Modifier = Modifier,
    supportingContent: @Composable (() -> Unit)? = null,
) {
    ListItem(
        headlineContent = headlineContent,
        supportingContent = supportingContent,
        trailingContent = {
            Switch(checked = checked, onCheckedChange = onCheckedChange)
        },
        modifier = modifier.clickable {
            onCheckedChange(!checked)
        }
    )
}


@Composable
fun EditSpecialFeedScreen(feedId: Long, viewModel: EditSpecialFeedViewModel = dfmHiltViewModel { factory: EditSpecialFeedViewModel.Factory ->
    factory.create(feedId)
}) {
    val uiState by viewModel.uiState.collectAsStateWithLifecycle()
    val context = LocalContext.current
    EditSpecialFeedScreen(uiState = uiState,
        onCreateShortcutToFeed = {
            viewModel.createShortcut(context, it)
        })
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun EditSpecialFeedScreen(
    uiState: EditFeedUiState,
    onCreateShortcutToFeed: (String) -> Unit
) {
    val feedTitle = run {
        val titleRes = when (uiState.feed?.id) {
            Feed.FEED_ID_FRESH -> appR.string.label_fresh_feeds_title
            Feed.FEED_ID_STARRED -> appR.string.label_starred_feeds_title
            Feed.FEED_ID_ALL_ARTICLES -> appR.string.label_all_articles_feeds_title
            else -> null
        }
        titleRes?.let { stringResource(it) }
            ?: uiState.feed?.displayTitle?.takeIf { it.isNotBlank() }
            ?: uiState.feed?.title ?: ""
    }

    Scaffold(topBar = {
        LargeTopAppBar(
            title = {
                Row(verticalAlignment = Alignment.CenterVertically) {
                    val feed = uiState.feed
                    if (feed != null) {
                        val iconVector = when {
                            feed.isArchivedFeed -> AppTheme3.Icons.Inventory2
                            feed.isStarredFeed -> AppTheme3.Icons.Star
                            feed.isPublishedFeed -> AppTheme3.Icons.CheckBox
                            feed.isFreshFeed -> AppTheme3.Icons.LocalCafe
                            feed.isAllArticlesFeed -> AppTheme3.Icons.FolderOpen
                            else -> null
                        }
                        if (iconVector != null) {
                            Icon(iconVector, contentDescription = null,
                                modifier = Modifier
                                    .padding(end = 16.dp)
                                    .size(48.dp))
                        }
                    }

                    Text(feedTitle,
                        overflow = TextOverflow.Ellipsis,
                        maxLines = 2,
                    )
                }
            },
            colors = TopAppBarDefaults.topAppBarColors(
                containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp)
            ))
    }) {
        Column(
            Modifier
                .padding(it)
                .padding(16.dp)
                .fillMaxWidth()
        ) {
            if (uiState.feed != null) {
                val feed = uiState.feed
                val specialFeedDescriptionId = when {
                    feed.isStarredFeed -> R.string.lbl_starred_articles_feed_description
                    feed.isFreshFeed -> R.string.lbl_fresh_articles_feed_description
                    feed.isAllArticlesFeed -> R.string.lbl_all_articles_feed_description
                    else -> null
                }
                val description = specialFeedDescriptionId?.let { stringResource(it) } ?: ""
                Text(description)

                if (uiState.canCreatePinShortcut) {
                    CreateShortcutButton(
                        onClick = { onCreateShortcutToFeed(feedTitle) },
                        modifier = Modifier
                            .align(Alignment.End)
                            .padding(top = 16.dp)
                    )
                }
            }
        }
    }
}

@Composable
private fun CreateShortcutButton(onClick: () -> Unit, modifier: Modifier = Modifier) {
    OutlinedButton(
        modifier = modifier,
        contentPadding = ButtonDefaults.ButtonWithIconContentPadding,
        onClick = onClick) {
        Row {
            Icon(
                AppTheme3.IconsAutoMirrored.AddToHomeScreen,
                contentDescription = null,
                modifier = Modifier.size(ButtonDefaults.IconSize)
            )
            Spacer(Modifier.width(ButtonDefaults.IconSpacing))
            Text(stringResource(R.string.btn_create_shortcut))
        }
    }
}


@Composable
fun ConfirmUnsubscribeDialog(
    feed: Feed,
    onDismissRequest: () -> Unit,
    onConfirmClick: () -> Unit
) {
    AlertDialog(
        onDismissRequest = onDismissRequest,
        confirmButton = {
            TextButton(onClick = onConfirmClick) {
                Text(stringResource(R.string.btn_confirm))
            }
        },
        dismissButton = {
            TextButton(onClick = onDismissRequest) {
                Text(stringResource(R.string.btn_cancel))
            }
        },
        title = {
            Text(stringResource(R.string.fragment_confirmation_title),
                style = MaterialTheme.typography.titleLarge)
        },
        text = {
            Column {
                Text(stringResource(R.string.lbl_unsubscribe_msg), style = MaterialTheme.typography.bodyLarge)
                Text(feed.title, style = MaterialTheme.typography.bodyLarge, fontWeight = FontWeight.Bold,
                    modifier = Modifier.padding(top = 24.dp))
                Text(feed.url)
            }
        })
}

@Preview
@Composable
private fun PreviewConfirmUnsubscribeDialog() {
    AppTheme3 {
        val feed = Feed(
            id = 4,
            title = "LinuxFr",
            url = "https://linuxfr.org/feed",
            unreadCount = 8,
        )
        ConfirmUnsubscribeDialog(feed,
            onDismissRequest = {},
            onConfirmClick = {})
    }
}

@Preview
@Composable
private fun PreviewEditFeedScreen() {
    AppTheme3 {
        val uiState = EditFeedUiState(
            feed = Feed(
                title = "Android developers",
                url = "https://medium.com/feed/androiddevelopers"
            ),
            faviconUrl = null,
            canCreatePinShortcut = true
        )

        EditFeedScreen(uiState, onUnsubscribeFeed = {}, onCreateShortcutToFeed = {}, onSyncAutomaticallyChange = {})
    }
}

@Preview
@Composable
private fun PreviewEditSpecialFeedScreen() {
    AppTheme3 {
        val uiState = EditFeedUiState(
            feed = Feed(
                id = Feed.FEED_ID_FRESH,
                title = "Fresh articles",
                url = "https://medium.com/feed/androiddevelopers"
            ),
            faviconUrl = null,
            canCreatePinShortcut = true
        )

        EditSpecialFeedScreen(uiState, onCreateShortcutToFeed = {})
    }
}