/*
 * Copyright 2026 Signal Messenger, LLC
 * SPDX-License-Identifier: AGPL-3.0-only
 */

package org.signal.core.ui.permissions

import android.content.Intent
import android.net.Uri
import android.provider.Settings
import androidx.annotation.StringRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.InlineTextContent
import androidx.compose.foundation.text.appendInlineContent
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.Placeholder
import androidx.compose.ui.text.PlaceholderVerticalAlign
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.os.bundleOf
import org.signal.core.ui.R
import org.signal.core.ui.compose.BottomSheets
import org.signal.core.ui.compose.Buttons
import org.signal.core.ui.compose.ComposeBottomSheetDialogFragment
import org.signal.core.ui.compose.DayNightPreviews
import org.signal.core.ui.compose.Previews
import org.signal.core.ui.compose.dismissWithAnimation

private const val PLACEHOLDER = "__RADIO_BUTTON_PLACEHOLDER__"

/**
 * Bottom sheet shown when a permission has been previously denied
 *
 * Displays rationale for the need of a permission and how to grant it
 */
class PermissionDeniedBottomSheet private constructor() : ComposeBottomSheetDialogFragment() {

  override val peekHeightPercentage: Float = 0.66f

  companion object {
    private const val ARG_TITLE = "argument.title_res"
    private const val ARG_SUBTITLE = "argument.subtitle_res"
    private const val ARG_USE_EXTENDED = "argument.use.extended"

    @JvmStatic
    fun showPermissionFragment(titleRes: Int, subtitleRes: Int, useExtended: Boolean = false): ComposeBottomSheetDialogFragment {
      return PermissionDeniedBottomSheet().apply {
        arguments = bundleOf(
          ARG_TITLE to titleRes,
          ARG_SUBTITLE to subtitleRes,
          ARG_USE_EXTENDED to useExtended
        )
      }
    }
  }

  @Composable
  override fun SheetContent() {
    PermissionDeniedSheetContent(
      titleRes = remember { requireArguments().getInt(ARG_TITLE) },
      subtitleRes = remember { requireArguments().getInt(ARG_SUBTITLE) },
      useExtended = remember { requireArguments().getBoolean(ARG_USE_EXTENDED) },
      onSettingsClicked = this::goToSettings
    )
  }

  private fun goToSettings() {
    val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
      data = Uri.fromParts("package", requireContext().packageName, null)
    }
    requireContext().startActivity(intent)
    dismissAllowingStateLoss()
  }
}

/**
 * Compose-native counterpart to [PermissionDeniedBottomSheet], for callers that live in a composition rather than
 * a fragment. Renders the same [PermissionDeniedSheetContent], so the two routes cannot drift apart.
 */
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun PermissionDeniedSheet(
  @StringRes titleRes: Int,
  @StringRes subtitleRes: Int,
  useExtended: Boolean = false,
  onDismiss: () -> Unit
) {
  val context = LocalContext.current
  val sheetState = rememberModalBottomSheetState()
  val scope = rememberCoroutineScope()

  // The content draws its own handle, so the sheet does not add a second one.
  BottomSheets.BottomSheet(
    onDismissRequest = { sheetState.dismissWithAnimation(scope, onComplete = onDismiss) },
    sheetState = sheetState,
    dragHandle = null
  ) {
    PermissionDeniedSheetContent(
      titleRes = titleRes,
      subtitleRes = subtitleRes,
      useExtended = useExtended,
      onSettingsClicked = {
        context.startActivity(Permissions.getApplicationSettingsIntent(context))
        sheetState.dismissWithAnimation(scope, onComplete = onDismiss)
      }
    )
  }
}

@DayNightPreviews
@Composable
private fun PermissionDeniedSheetContentPreview() {
  Previews.BottomSheetContentPreview {
    PermissionDeniedSheetContent(
      titleRes = R.string.PermissionDeniedBottomSheet__settings,
      subtitleRes = R.string.PermissionDeniedBottomSheet__1_tap_settings_below,
      onSettingsClicked = {}
    )
  }
}

@Composable
fun PermissionDeniedSheetContent(
  @StringRes titleRes: Int,
  @StringRes subtitleRes: Int,
  useExtended: Boolean = false,
  onSettingsClicked: () -> Unit
) {
  Column(
    modifier = Modifier.padding(start = 24.dp, end = 24.dp, top = 12.dp, bottom = 32.dp)
  ) {
    BottomSheets.Handle(
      modifier = Modifier.align(Alignment.CenterHorizontally)
    )

    Text(
      text = stringResource(titleRes),
      style = MaterialTheme.typography.headlineSmall,
      color = MaterialTheme.colorScheme.onSurface,
      textAlign = TextAlign.Center,
      modifier = Modifier
        .align(Alignment.CenterHorizontally)
        .padding(bottom = 12.dp, top = 20.dp)
    )

    Text(
      text = stringResource(subtitleRes),
      style = MaterialTheme.typography.bodyMedium,
      color = MaterialTheme.colorScheme.onSurface,
      modifier = Modifier
        .align(Alignment.CenterHorizontally)
        .padding(bottom = 32.dp)
    )

    Text(
      text = stringResource(R.string.PermissionDeniedBottomSheet__1_tap_settings_below),
      color = MaterialTheme.colorScheme.onSurface,
      modifier = Modifier.padding(bottom = 24.dp)
    )

    if (useExtended) {
      Text(
        text = stringResource(R.string.PermissionDeniedBottomSheet__2_tap_permissions),
        color = MaterialTheme.colorScheme.onSurface,
        modifier = Modifier.padding(bottom = 24.dp)
      )
    }

    val stringId = if (useExtended) R.string.PermissionDeniedBottomSheet__3_allow_permission else R.string.PermissionDeniedBottomSheet__2_allow_permission
    val stepString = stringResource(id = stringId, PLACEHOLDER)
    val (stepText, stepInlineContent) = remember(stepString) {
      val parts = stepString.split(PLACEHOLDER)
      val annotatedString = buildAnnotatedString {
        append(parts[0])
        appendInlineContent("radio")
        append(parts[1])
      }

      val inlineContentMap = mapOf(
        "radio" to InlineTextContent(Placeholder(22.sp, 22.sp, PlaceholderVerticalAlign.Center)) {
          Image(
            imageVector = ImageVector.vectorResource(id = R.drawable.ic_radio_button_checked),
            contentDescription = null,
            modifier = Modifier.fillMaxSize()
          )
        }
      )

      annotatedString to inlineContentMap
    }

    Text(
      text = stepText,
      inlineContent = stepInlineContent,
      color = MaterialTheme.colorScheme.onSurface,
      modifier = Modifier.padding(bottom = 32.dp)
    )

    Buttons.LargeTonal(
      onClick = onSettingsClicked,
      modifier = Modifier
        .align(Alignment.CenterHorizontally)
        .fillMaxWidth(1f)
    ) {
      Text(text = stringResource(id = R.string.PermissionDeniedBottomSheet__settings))
    }
  }
}
