package ru.forbion.f7cloud.feature.talk

import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
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.ui.draw.clip
import androidx.compose.ui.input.pointer.PointerEventPass
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
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.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import kotlinx.coroutines.launch
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
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.unit.dp
import androidx.core.content.ContextCompat
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewmodel.compose.viewModel
import coil.compose.AsyncImage
import ru.forbion.f7cloud.core.auth.AuthSession
import ru.forbion.f7cloud.core.designsystem.F7Colors
import ru.forbion.f7cloud.core.designsystem.F7ModuleScreen
import ru.forbion.f7cloud.core.designsystem.F7OverlayDismissHandler
import ru.forbion.f7cloud.core.designsystem.F7TextButton
import ru.forbion.f7cloud.feature.talknative.TalkNativeCallContext
import ru.forbion.f7cloud.feature.talknative.TalkNativeCallLauncher

@Composable
fun TalkScreen(
    session: AuthSession,
    modifier: Modifier = Modifier,
    openRoomToken: String? = null,
    scrollToMessageId: Long? = null,
    chatsListRequest: Int = 0,
    pushSyncRequest: Int = 0,
    pushRoomToken: String? = null,
    onOpenRoomConsumed: () -> Unit = {},
    onRoomOpenStateChange: (Boolean) -> Unit = {},
    onOpenCalendar: () -> Unit = {},
    onUnauthorized: () -> Unit = {},
) {
    val context = LocalContext.current
    val offlineRepo = remember { TalkOfflineRepository(context) }
    val vm: TalkViewModel = viewModel(
        factory = object : ViewModelProvider.Factory {
            @Suppress("UNCHECKED_CAST")
            override fun <T : ViewModel> create(modelClass: Class<T>): T =
                TalkViewModel(offlineRepository = offlineRepo) as T
        },
    )
    val state by vm.state.collectAsState()
    var draft by rememberSaveable { mutableStateOf("") }
    val callPermissions = remember {
        arrayOf(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO)
    }
    var pendingCallContext by remember { mutableStateOf<TalkNativeCallContext?>(null) }
    val permissionLauncher = rememberLauncherForActivityResult(
        ActivityResultContracts.RequestMultiplePermissions(),
    ) { grants ->
        val callContext = pendingCallContext
        pendingCallContext = null
        if (callContext != null && callPermissions.all { grants[it] == true }) {
            TalkNativeCallLauncher.launchRoomCall(context, session, callContext)
        }
    }

    LaunchedEffect(session.serverUrl, session.username) {
        vm.load(session)
    }
    LaunchedEffect(openRoomToken, scrollToMessageId) {
        if (!openRoomToken.isNullOrBlank()) {
            vm.openRoom(session, openRoomToken, scrollToMessageId)
            onOpenRoomConsumed()
        }
    }
    LaunchedEffect(state.unauthorized) {
        if (state.unauthorized) onUnauthorized()
    }
    LaunchedEffect(chatsListRequest) {
        if (chatsListRequest > 0 && state.selectedRoomToken != null) {
            vm.closeRoom(session)
        }
    }
    LaunchedEffect(pushSyncRequest) {
        if (pushSyncRequest > 0) {
            vm.syncFromPush(session, pushRoomToken)
        }
    }
    LaunchedEffect(state.selectedRoomToken) {
        onRoomOpenStateChange(state.selectedRoomToken != null)
    }

    F7OverlayDismissHandler(
        enabled = state.selectedRoomToken != null,
        onDismiss = { vm.closeRoom(session) },
    )
    F7OverlayDismissHandler(
        enabled = state.showCreateRoom,
        onDismiss = vm::closeCreateRoom,
    )
    F7OverlayDismissHandler(
        enabled = state.showParticipants,
        onDismiss = vm::closeParticipants,
    )
    F7OverlayDismissHandler(
        enabled = state.showFilterSheet,
        onDismiss = vm::closeFilterSheet,
    )
    F7OverlayDismissHandler(
        enabled = state.showRoomInfo,
        onDismiss = vm::closeRoomInfo,
    )

    fun startCall() {
        val room = state.selectedRoom ?: return
        val callContext = TalkNativeCallContext(
            roomToken = room.token,
            displayName = room.displayName,
            isOneToOne = room.type == 1 || room.type == 3,
            joinExistingCall = room.hasCall,
        )
        val missing = callPermissions.filter {
            ContextCompat.checkSelfPermission(context, it) != PackageManager.PERMISSION_GRANTED
        }
        if (missing.isEmpty()) {
            TalkNativeCallLauncher.launchRoomCall(context, session, callContext)
        } else {
            pendingCallContext = callContext
            permissionLauncher.launch(missing.toTypedArray())
        }
    }

    TalkCreateRoomSheet(
        visible = state.showCreateRoom,
        session = session,
        mode = state.createRoomMode,
        roomName = state.createRoomName,
        userSearchQuery = state.userSearchQuery,
        userSearchResults = state.userSearchResults,
        selectedUsers = state.selectedUsers,
        userSearchLoading = state.userSearchLoading,
        creating = state.creatingRoom,
        onDismiss = vm::closeCreateRoom,
        onModeChange = vm::setCreateRoomMode,
        onRoomNameChange = vm::setCreateRoomName,
        onUserSearchChange = { vm.setUserSearchQuery(session, it) },
        onUserToggle = vm::toggleUserSelection,
        onCreate = { vm.createRoom(session) },
    )

    TalkParticipantsSheet(
        visible = state.showParticipants,
        session = session,
        participants = state.participants,
        loading = state.participantsLoading,
        onDismiss = vm::closeParticipants,
    )

    TalkFilterSheet(
        visible = state.showFilterSheet,
        selected = state.roomFilter,
        onDismiss = vm::closeFilterSheet,
        onFilterSelected = vm::setRoomFilter,
    )

    TalkRoomInfoSheet(
        visible = state.showRoomInfo,
        session = session,
        state = state,
        onDismiss = vm::closeRoomInfo,
        onSearch = { vm.searchMessagesInRoom(session) },
        onSearchQueryChange = vm::setMessageSearchQuery,
        onAddParticipant = { vm.addParticipantToRoom(session, it) },
        onNotificationLevel = { vm.setRoomNotificationLevel(session, it) },
        onLoadParticipants = { vm.openParticipants(session) },
    )

    if (state.selectedRoomToken != null) {
        val pickAttachment = rememberLauncherForActivityResult(
            contract = ActivityResultContracts.GetContent(),
        ) { uri ->
            uri?.let {
                val cap = draft.trim()
                vm.uploadAttachment(context.applicationContext, session, it, cap)
                if (cap.isNotEmpty()) draft = ""
            }
        }
        val voiceRecorder = remember { TalkVoiceRecorder(context.applicationContext) }
        var voiceRecording by remember { mutableStateOf(false) }
        val audioPermissionLauncher = rememberLauncherForActivityResult(
            ActivityResultContracts.RequestPermission(),
        ) { granted ->
            if (granted) {
                runCatching { voiceRecorder.start() }
                voiceRecording = voiceRecorder.isRecording
            }
        }
        fun toggleVoiceRecording() {
            if (voiceRecording) {
                val file = voiceRecorder.stop()
                voiceRecording = false
                if (file != null) {
                    vm.uploadVoiceRecording(session, file)
                }
            } else if (ContextCompat.checkSelfPermission(
                    context,
                    Manifest.permission.RECORD_AUDIO,
                ) == PackageManager.PERMISSION_GRANTED
            ) {
                runCatching { voiceRecorder.start() }
                voiceRecording = voiceRecorder.isRecording
            } else {
                audioPermissionLauncher.launch(Manifest.permission.RECORD_AUDIO)
            }
        }
        Box(modifier = modifier.fillMaxSize()) {
            if (state.loading && state.messages.isEmpty()) {
                CircularProgressIndicator(
                    color = F7Colors.Primary,
                    modifier = Modifier
                        .align(Alignment.Center)
                        .padding(16.dp),
                )
            }
            if (!state.error.isNullOrBlank()) {
                Text(
                    state.error ?: "",
                    color = F7Colors.Error,
                    style = MaterialTheme.typography.bodyMedium,
                    modifier = Modifier
                        .align(Alignment.TopCenter)
                        .padding(16.dp),
                )
            }
            ChatContent(
                session = session,
                state = state,
                draft = draft,
                onDraftChange = { draft = it },
                onSend = {
                    vm.sendMessage(session, draft)
                    draft = ""
                },
                onAttach = { pickAttachment.launch("*/*") },
                onVoiceToggle = { toggleVoiceRecording() },
                voiceRecording = voiceRecording,
                onRetry = { vm.refreshMessages(session) },
                onStartCall = { startCall() },
                onBackClick = { vm.closeRoom(session) },
                onOpenRoomInfo = {
                    vm.openRoomInfo()
                    vm.openParticipants(session)
                },
                onScheduleMeeting = onOpenCalendar,
                onToggleSystemMessages = vm::toggleSystemMessagesCollapse,
                onReply = { vm.setReplyTo(it) },
                onReaction = { msg, emoji -> vm.addReaction(session, msg.id, emoji) },
                onDeleteMessage = { vm.deleteMessage(session, it.id) },
                onEditMessage = { msg, text -> vm.editMessage(session, msg.id, text) },
                onMentionQuery = { vm.setMentionQuery(session, it) },
                onMentionSelected = { mention ->
                    draft = insertMentionIntoDraft(draft, mention)
                    vm.clearMentionCandidates()
                },
                onClearReply = { vm.setReplyTo(null) },
                onClearHighlight = vm::clearHighlightMessage,
            )
        }
    } else {
        F7ModuleScreen(
            modifier = modifier,
            loading = state.loading && state.rooms.isEmpty(),
            error = state.error,
            onErrorRetry = { vm.load(session) },
        ) {
            RoomListContent(
                session = session,
                rooms = state.filteredRooms,
                searchQuery = state.searchQuery,
                roomFilter = state.roomFilter,
                filterActive = state.activeFilterCount > 0,
                onSearchChange = vm::setSearchQuery,
                onFilterClick = vm::openFilterSheet,
                onClearFilter = { vm.setRoomFilter(TalkRoomFilter.ALL) },
                onRoomClick = { vm.openRoom(session, it.token) },
                onCreateRoom = vm::openCreateRoom,
                onToggleFavorite = { vm.toggleFavorite(session, it.token) },
                onMarkUnread = { vm.markRoomUnread(session, it.token) },
                refreshing = state.loading && state.rooms.isNotEmpty(),
                onRefresh = { vm.load(session) },
            )
        }
    }
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun RoomListContent(
    session: AuthSession,
    rooms: List<TalkRoom>,
    searchQuery: String,
    roomFilter: TalkRoomFilter,
    filterActive: Boolean,
    onSearchChange: (String) -> Unit,
    onFilterClick: () -> Unit,
    onClearFilter: () -> Unit,
    onRoomClick: (TalkRoom) -> Unit,
    onCreateRoom: () -> Unit,
    onToggleFavorite: (TalkRoom) -> Unit,
    onMarkUnread: (TalkRoom) -> Unit,
    refreshing: Boolean = false,
    onRefresh: () -> Unit = {},
) {
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.spacedBy(8.dp),
    ) {
        TalkListToolbar(
            session = session,
            query = searchQuery,
            onQueryChange = onSearchChange,
            filterActive = filterActive,
            onFilterClick = onFilterClick,
            onCreateClick = onCreateRoom,
        )
        TalkFilterChips(selected = roomFilter, onClear = onClearFilter)
        PullToRefreshBox(
            isRefreshing = refreshing,
            onRefresh = onRefresh,
            modifier = Modifier
                .weight(1f)
                .fillMaxWidth(),
        ) {
        if (rooms.isEmpty()) {
            Column(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(top = 32.dp),
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.spacedBy(12.dp),
            ) {
                AsyncImage(
                    model = TalkAssets.spreed(session, "icon-empty-mail-glass.svg"),
                    contentDescription = null,
                    modifier = Modifier.size(64.dp),
                    contentScale = ContentScale.Fit,
                )
                Text(
                    when {
                        searchQuery.isNotBlank() -> "Ничего не найдено"
                        roomFilter != TalkRoomFilter.ALL -> "Нет комнат по фильтру"
                        else -> "Нет комнат"
                    },
                    style = MaterialTheme.typography.bodyMedium,
                    color = F7Colors.TextSecondary,
                )
            }
        } else {
            LazyColumn(
                modifier = Modifier.fillMaxSize(),
                verticalArrangement = Arrangement.spacedBy(4.dp),
            ) {
                items(rooms, key = { it.token }) { room ->
                    TalkRoomRow(
                        room = room,
                        session = session,
                        onClick = { onRoomClick(room) },
                        onToggleFavorite = { onToggleFavorite(room) },
                        onMarkUnread = { onMarkUnread(room) },
                    )
                }
            }
        }
        }
    }
}

@Composable
private fun ChatContent(
    session: AuthSession,
    state: TalkUiState,
    draft: String,
    onDraftChange: (String) -> Unit,
    onSend: () -> Unit,
    onAttach: () -> Unit,
    onVoiceToggle: () -> Unit,
    voiceRecording: Boolean,
    onRetry: () -> Unit,
    onStartCall: () -> Unit,
    onBackClick: () -> Unit,
    onOpenRoomInfo: () -> Unit,
    onScheduleMeeting: () -> Unit,
    onToggleSystemMessages: () -> Unit,
    onReply: (TalkMessage) -> Unit,
    onReaction: (TalkMessage, String) -> Unit,
    onDeleteMessage: (TalkMessage) -> Unit,
    onEditMessage: (TalkMessage, String) -> Unit,
    onMentionQuery: (String?) -> Unit,
    onMentionSelected: (TalkMention) -> Unit,
    onClearReply: () -> Unit,
    onClearHighlight: () -> Unit,
) {
    var actionMessage by remember { mutableStateOf<TalkMessage?>(null) }
    var pendingDelete by remember { mutableStateOf<TalkMessage?>(null) }
    var editingMessage by remember { mutableStateOf<TalkMessage?>(null) }
    var editDraft by remember { mutableStateOf("") }
    val listState = rememberLazyListState()
    val scope = rememberCoroutineScope()
    val chatItems = remember(state.messages, state.openRoomLastReadMessageId, state.collapseSystemMessages) {
        buildChatItems(
            messages = state.messages,
            lastReadMessageId = state.openRoomLastReadMessageId,
            selfActorId = session.username,
            collapseSystemMessages = state.collapseSystemMessages,
        )
    }
    val systemMessageCount = remember(state.messages) {
        state.messages.count { it.isSystemMessage }
    }
    val showScrollDown by remember {
        derivedStateOf {
            if (chatItems.isEmpty()) return@derivedStateOf false
            val info = listState.layoutInfo
            val lastVisible = info.visibleItemsInfo.lastOrNull()?.index ?: 0
            lastVisible < info.totalItemsCount - 1
        }
    }
    var stickToBottom by remember { mutableStateOf(true) }

    LaunchedEffect(showScrollDown) {
        stickToBottom = !showScrollDown
    }

    LaunchedEffect(chatItems.size, state.highlightMessageId) {
        val targetId = state.highlightMessageId ?: return@LaunchedEffect
        val idx = chatItems.indexOfFirst { item ->
            item is TalkChatItem.MessageGroup && item.messages.any { it.id == targetId }
        }
        if (idx >= 0) {
            listState.animateScrollToItem(idx)
            kotlinx.coroutines.delay(2500)
            onClearHighlight()
        }
    }

    LaunchedEffect(chatItems.size) {
        if (chatItems.isNotEmpty() && stickToBottom) {
            listState.animateScrollToItem(chatItems.lastIndex)
        }
    }

    TalkMessageActionSheet(
        message = actionMessage,
        canModify = actionMessage?.let { msg ->
            msg.actorId.isNotBlank() && msg.actorId == session.username && !msg.isSystemMessage
        } == true,
        onDismiss = { actionMessage = null },
        onReply = {
            onReply(it)
            actionMessage = null
        },
        onEdit = {
            editingMessage = it
            editDraft = it.text
            actionMessage = null
        },
        onDelete = {
            pendingDelete = it
            actionMessage = null
        },
        onReaction = { msg, emoji ->
            onReaction(msg, emoji)
            actionMessage = null
        },
    )

    editingMessage?.let { message ->
        AlertDialog(
            onDismissRequest = { editingMessage = null },
            title = { Text("Изменить сообщение") },
            text = {
                OutlinedTextField(
                    value = editDraft,
                    onValueChange = { editDraft = it },
                    minLines = 2,
                    maxLines = 6,
                )
            },
            confirmButton = {
                TextButton(onClick = {
                    onEditMessage(message, editDraft)
                    editingMessage = null
                }) {
                    Text("Сохранить", color = F7Colors.Primary)
                }
            },
            dismissButton = {
                TextButton(onClick = { editingMessage = null }) {
                    Text("Отмена")
                }
            },
        )
    }

    pendingDelete?.let { message ->
        AlertDialog(
            onDismissRequest = { pendingDelete = null },
            title = { Text("Удалить сообщение?") },
            text = { Text("Сообщение будет удалено для всех участников.") },
            confirmButton = {
                TextButton(onClick = {
                    onDeleteMessage(message)
                    pendingDelete = null
                }) {
                    Text("Удалить", color = F7Colors.Primary)
                }
            },
            dismissButton = {
                TextButton(onClick = { pendingDelete = null }) {
                    Text("Отмена")
                }
            },
        )
    }

    Box(modifier = Modifier.fillMaxSize()) {
        TalkChatBackground(
            session = session,
            modifier = Modifier.fillMaxSize(),
        )
        Column(
            modifier = Modifier.fillMaxSize(),
        ) {
            Column(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(horizontal = 16.dp, vertical = 8.dp),
                verticalArrangement = Arrangement.spacedBy(8.dp),
            ) {
                state.selectedRoom?.let { room ->
                    TalkChatHeader(
                        session = session,
                        room = room,
                        participantCount = state.participants.takeIf { it.isNotEmpty() }?.size,
                        onBackClick = onBackClick,
                        onStartCall = onStartCall,
                        onMenuClick = onOpenRoomInfo,
                        onScheduleMeeting = onScheduleMeeting,
                    )
                }
                if (systemMessageCount > 0) {
                    TalkSystemMessagesToggle(
                        collapsed = state.collapseSystemMessages,
                        systemCount = systemMessageCount,
                        session = session,
                        onClick = onToggleSystemMessages,
                    )
                }
                if (state.messages.any { it.status == TalkMessage.Status.FAILED }) {
                    F7TextButton(text = "Повторить загрузку", onClick = onRetry)
                }
            }
            LazyColumn(
                modifier = Modifier
                    .weight(1f)
                    .fillMaxWidth()
                    .padding(horizontal = 8.dp)
                    .pointerInput(Unit) {
                        awaitEachGesture {
                            awaitFirstDown(pass = PointerEventPass.Initial)
                            stickToBottom = false
                        }
                    },
                state = listState,
                verticalArrangement = Arrangement.spacedBy(4.dp),
            ) {
                items(chatItems.size, key = { index ->
                    when (val item = chatItems[index]) {
                        is TalkChatItem.DateSeparator -> "date-${item.label}-$index"
                        TalkChatItem.UnreadMarker -> "unread-$index"
                        is TalkChatItem.SystemLine -> "sys-${item.text}-$index"
                        is TalkChatItem.MessageGroup -> "grp-${item.messages.first().id}-$index"
                    }
                }) { index ->
                    when (val item = chatItems[index]) {
                        is TalkChatItem.DateSeparator -> TalkDateSeparator(item.label)
                        TalkChatItem.UnreadMarker -> TalkUnreadMarker()
                        is TalkChatItem.SystemLine -> Text(
                            item.text,
                            modifier = Modifier
                                .fillMaxWidth()
                                .padding(vertical = 4.dp),
                            style = MaterialTheme.typography.labelSmall,
                            color = F7Colors.TextSecondary,
                        )
                        is TalkChatItem.MessageGroup -> TalkMessageGroupRow(
                            session = session,
                            group = item,
                            highlightMessageId = state.highlightMessageId,
                            onLongPress = { actionMessage = it },
                            onReaction = onReaction,
                        )
                    }
                }
            }
            Column(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(horizontal = 16.dp, vertical = 8.dp)
                    .navigationBarsPadding(),
            ) {
                TalkReplyPreviewBar(message = state.replyToMessage, onClear = onClearReply)
                TalkComposer(
                    session = session,
                    value = draft,
                    onValueChange = onDraftChange,
                    onSend = onSend,
                    onAttach = onAttach,
                    onVoiceToggle = onVoiceToggle,
                    voiceRecording = voiceRecording,
                    mentionCandidates = state.mentionCandidates,
                    onMentionQuery = onMentionQuery,
                    onMentionSelected = onMentionSelected,
                    sending = state.sending,
                    uploading = state.uploading,
                )
            }
        }
        TalkScrollToBottomFab(
            session = session,
            visible = showScrollDown,
            onClick = {
                stickToBottom = true
                if (chatItems.isNotEmpty()) {
                    scope.launch { listState.animateScrollToItem(chatItems.lastIndex) }
                }
            },
            modifier = Modifier
                .align(Alignment.BottomEnd)
                .padding(end = 16.dp, bottom = 16.dp),
        )
    }
}
