package ru.forbion.f7cloud.feature.calendar

import android.Manifest
import android.content.pm.PackageManager
import android.net.Uri
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ModalDrawerSheet
import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.rememberDrawerState
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.snapshotFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch
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.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat
import androidx.lifecycle.viewmodel.compose.viewModel
import kotlinx.coroutines.delay
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 java.time.format.DateTimeFormatter
import java.util.Locale

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CalendarScreen(
    session: AuthSession,
    modifier: Modifier = Modifier,
    focusEventUid: String? = null,
    settingsRequest: Int = 0,
    sidebarOpen: Boolean = false,
    onSidebarOpenChange: (Boolean) -> Unit = {},
    onFocusEventConsumed: () -> Unit = {},
    onUnauthorized: () -> Unit = {},
) {
    val context = LocalContext.current
    val vm: CalendarViewModel = viewModel(factory = CalendarViewModel.Factory(context))
    val state by vm.state.collectAsState()

    val permissionLauncher = rememberLauncherForActivityResult(
        ActivityResultContracts.RequestMultiplePermissions(),
    ) { result ->
        vm.onCalendarPermissionResult(result.values.all { it })
    }

    val importLauncher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
        if (uri == null) return@rememberLauncherForActivityResult
        runCatching {
            context.contentResolver.openInputStream(uri)?.bufferedReader()?.use { it.readText() }
        }.onSuccess { content ->
            if (!content.isNullOrBlank()) vm.importIcs(content)
        }.onFailure {
            vm.updateEventDraft { it } // no-op; snack via error path if needed
        }
    }

    fun hasCalendarPermission(): Boolean {
        val read = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CALENDAR) ==
            PackageManager.PERMISSION_GRANTED
        val write = ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_CALENDAR) ==
            PackageManager.PERMISSION_GRANTED
        return read && write
    }

    fun requestCalendarPermissions() {
        permissionLauncher.launch(
            arrayOf(Manifest.permission.READ_CALENDAR, Manifest.permission.WRITE_CALENDAR),
        )
    }

    val drawerState = rememberDrawerState(DrawerValue.Closed)
    val scope = rememberCoroutineScope()

    LaunchedEffect(sidebarOpen) {
        if (sidebarOpen && drawerState.isClosed) drawerState.open()
        if (!sidebarOpen && drawerState.isOpen) drawerState.close()
    }
    LaunchedEffect(drawerState) {
        snapshotFlow { drawerState.isOpen }.distinctUntilChanged().collect { open ->
            if (open != sidebarOpen) onSidebarOpenChange(open)
        }
    }

    LaunchedEffect(session.serverUrl, session.username, session.davUserId) {
        vm.bind(session)
    }
    LaunchedEffect(focusEventUid) {
        val uid = focusEventUid ?: return@LaunchedEffect
        vm.focusEventByUid(session, uid)
        onFocusEventConsumed()
    }
    LaunchedEffect(settingsRequest) {
        if (settingsRequest > 0) vm.openSyncSettings()
    }
    LaunchedEffect(state.unauthorized) {
        if (state.unauthorized) onUnauthorized()
    }

    F7OverlayDismissHandler(
        enabled = state.eventDetail != null,
        onDismiss = vm::closeEventDetail,
    )
    F7OverlayDismissHandler(
        enabled = state.createDialogOpen,
        onDismiss = vm::closeCreateDialog,
    )
    F7OverlayDismissHandler(
        enabled = state.settingsOpen,
        onDismiss = vm::closeAppSettings,
    )
    F7OverlayDismissHandler(
        enabled = state.syncSettingsOpen,
        onDismiss = vm::closeSyncSettings,
    )
    F7OverlayDismissHandler(
        enabled = state.trashOpen,
        onDismiss = vm::closeTrash,
    )
    F7OverlayDismissHandler(
        enabled = state.tasksOpen,
        onDismiss = vm::closeTasks,
    )
    F7OverlayDismissHandler(
        enabled = state.createBookOpen,
        onDismiss = vm::closeCreateBook,
    )
    F7OverlayDismissHandler(
        enabled = state.datePickerOpen,
        onDismiss = vm::closeDatePicker,
    )

    if (state.syncSettingsOpen) {
        CalendarSyncSettingsDialog(
            state = state,
            onDismiss = vm::closeSyncSettings,
            onSyncEnabledChange = { enabled ->
                if (enabled && !hasCalendarPermission()) requestCalendarPermissions()
                vm.setSyncEnabled(enabled)
            },
            onSelectCalendar = vm::selectDeviceCalendar,
            onSyncNow = {
                if (!hasCalendarPermission()) requestCalendarPermissions() else vm.runSyncNow()
            },
        )
    }

    if (state.settingsOpen) {
        CalendarAppSettingsSheet(
            settings = state.userSettings,
            onDismiss = vm::closeAppSettings,
            onChange = vm::updateUserSettings,
            onImportClick = { importLauncher.launch("*/*") },
        )
    }

    if (state.trashOpen) {
        CalendarTrashSheet(
            items = state.trashItems,
            onDismiss = vm::closeTrash,
            onRestore = vm::restoreTrash,
            onPurge = vm::purgeTrash,
        )
    }

    if (state.tasksOpen) {
        CalendarTasksSheet(
            tasks = state.unscheduledTasks,
            onDismiss = vm::closeTasks,
        )
    }

    if (state.createBookOpen) {
        CalendarCreateBookDialog(
            draft = state.createBookDraft,
            saving = state.saving,
            onDismiss = vm::closeCreateBook,
            onDraftChange = vm::updateCreateBookDraft,
            onSave = vm::saveCreateBook,
        )
    }

    if (state.datePickerOpen) {
        CalendarDatePickerDialog(
            selectedDay = state.selectedDay,
            onDismiss = vm::closeDatePicker,
            onConfirm = vm::pickDate,
        )
    }

    if (state.createDialogOpen) {
        CalendarFullEventEditorDialog(
            state = state,
            onDismiss = vm::closeCreateDialog,
            onDraftChange = { draft -> vm.updateEventDraft { draft } },
            onSave = vm::saveEventDraft,
            onDelete = vm::deleteSelectedEvent,
            onSearchAttendees = vm::searchAttendees,
            onAddAttendee = vm::addAttendee,
            onRemoveAttendee = vm::removeAttendee,
            onSearchLocations = vm::searchLocations,
            onApplyLocation = vm::applyLocationSuggestion,
        )
    }

    state.eventDetail?.let { event ->
        CalendarEventDetailSheet(
            session = session,
            event = event,
            onDismiss = vm::closeEventDetail,
            onEdit = { vm.openEditEvent(event) },
            onDelete = vm::deleteSelectedEvent,
            onDuplicate = { vm.duplicateEvent(event) },
            onAccept = { vm.respondToInvite("ACCEPTED") },
            onDecline = { vm.respondToInvite("DECLINED") },
            onTentative = { vm.respondToInvite("TENTATIVE") },
            deleting = state.deleting,
        )
    }

    val visibleEvents = vm.visibleEvents()
    val eventsByDay = rememberEventsByDay(visibleEvents)

    ModalNavigationDrawer(
        drawerState = drawerState,
        gesturesEnabled = true,
        drawerContent = {
            ModalDrawerSheet(
                modifier = Modifier.fillMaxWidth(0.86f),
                drawerContainerColor = F7Colors.Surface,
            ) {
                CalendarNavigationSidebar(
                    session = session,
                    state = state,
                    periodTitle = vm.periodTitle(),
                    onPrevious = vm::previousPeriod,
                    onNext = vm::nextPeriod,
                    onPeriodClick = vm::openDatePicker,
                    onCreate = { vm.openCreateDialog() },
                    onToday = vm::goToToday,
                    onViewMode = vm::setViewMode,
                    onDismiss = {
                        scope.launch { drawerState.close() }
                        onSidebarOpenChange(false)
                    },
                    onToggleCalendar = vm::toggleCalendarVisibility,
                    onOpenSettings = {
                        onSidebarOpenChange(false)
                        vm.openSyncSettings()
                    },
                    onOpenAppSettings = {
                        onSidebarOpenChange(false)
                        vm.openAppSettings()
                    },
                    onOpenTrash = {
                        onSidebarOpenChange(false)
                        vm.openTrash()
                    },
                    onOpenTasks = {
                        onSidebarOpenChange(false)
                        vm.openTasks()
                    },
                    onCreateBook = {
                        onSidebarOpenChange(false)
                        vm.openCreateBook()
                    },
                    onSyncNow = {
                        if (!hasCalendarPermission()) requestCalendarPermissions() else vm.runSyncNow()
                    },
                )
            }
        },
    ) {
    F7ModuleScreen(
        modifier = modifier,
        loading = state.loading && state.events.isEmpty(),
        error = state.error,
    ) {
        if (state.syncEnabled) {
            Text(
                buildString {
                    append("Синхронизация с телефоном")
                    state.lastSyncLabel?.let { append(" · $it") }
                    if (state.syncing) append(" · идёт…")
                },
                style = MaterialTheme.typography.labelSmall,
                color = F7Colors.PrimaryDark,
            )
        }
        if (state.needsCalendarPermission && state.syncEnabled) {
            Text(
                "Нужен доступ к календарю телефона",
                style = MaterialTheme.typography.bodySmall,
                color = F7Colors.Error,
                modifier = Modifier
                    .clip(RoundedCornerShape(8.dp))
                    .padding(vertical = 4.dp),
            )
        }
        if (!state.snackMessage.isNullOrBlank()) {
            Text(
                state.snackMessage!!,
                color = F7Colors.PrimaryDark,
                style = MaterialTheme.typography.bodySmall,
                modifier = Modifier
                    .fillMaxWidth()
                    .clip(RoundedCornerShape(8.dp))
                    .padding(8.dp),
            )
            LaunchedEffect(state.snackMessage) {
                delay(2500)
                vm.clearSnack()
            }
        }

        Column(
            modifier = Modifier
                .weight(1f)
                .fillMaxWidth(),
        ) {
            when (state.viewMode) {
                CalendarViewMode.MONTH -> {
                    CalendarMonthGrid(
                        month = state.visibleMonth,
                        selectedDay = state.selectedDay,
                        eventsByDay = eventsByDay,
                        onSelectDay = vm::selectDay,
                    )
                    SelectedDayEvents(
                        day = state.selectedDay,
                        events = vm.eventsForDay(state.selectedDay),
                        focusedEventUid = state.focusedEventUid,
                        onEventClick = vm::openEventDetail,
                        modifier = Modifier.weight(1f),
                    )
                }
                CalendarViewMode.WEEK -> {
                    CalendarWeekView(
                        selectedDay = state.selectedDay,
                        events = visibleEvents,
                        onSelectDay = vm::selectDay,
                        onEventClick = vm::openEventDetail,
                    )
                    SelectedDayEvents(
                        day = state.selectedDay,
                        events = vm.eventsForDay(state.selectedDay),
                        focusedEventUid = state.focusedEventUid,
                        onEventClick = vm::openEventDetail,
                        modifier = Modifier.weight(1f),
                    )
                }
                CalendarViewMode.DAY -> {
                    CalendarDayTimeline(
                        session = session,
                        day = state.selectedDay,
                        events = vm.eventsForDay(state.selectedDay),
                        viewMode = state.viewMode,
                        onViewMode = vm::setViewMode,
                        onEventClick = vm::openEventDetail,
                        modifier = Modifier.weight(1f),
                    )
                }
                CalendarViewMode.YEAR -> {
                    CalendarYearView(
                        year = state.visibleYear,
                        selectedDay = state.selectedDay,
                        events = visibleEvents,
                        onSelectMonth = vm::selectYearMonth,
                        modifier = Modifier.weight(1f),
                    )
                }
                CalendarViewMode.LIST -> {
                    if (!state.loading && visibleEvents.isEmpty()) {
                        Text("Нет предстоящих событий", color = F7Colors.TextSecondary)
                    } else {
                        CalendarListView(
                            events = visibleEvents,
                            onEventClick = vm::openEventDetail,
                            modifier = Modifier.weight(1f),
                        )
                    }
                }
            }
        }
    }
    }
}

@Composable
private fun SelectedDayEvents(
    day: java.time.LocalDate,
    events: List<CalendarEventItem>,
    focusedEventUid: String?,
    onEventClick: (CalendarEventItem) -> Unit,
    modifier: Modifier = Modifier,
) {
    val formatter = DateTimeFormatter.ofPattern("d MMMM yyyy", Locale.forLanguageTag("ru"))
    Column(modifier = modifier.fillMaxWidth()) {
        Text(
            day.format(formatter).replaceFirstChar {
                if (it.isLowerCase()) it.titlecase(Locale.forLanguageTag("ru")) else it.toString()
            },
            style = MaterialTheme.typography.titleSmall,
            color = F7Colors.TextPrimary,
            modifier = Modifier.padding(top = 4.dp),
        )
        if (events.isEmpty()) {
            Text("Нет событий в этот день", style = MaterialTheme.typography.bodyMedium, color = F7Colors.TextSecondary)
        } else {
            LazyColumn(
                modifier = Modifier
                    .weight(1f)
                    .fillMaxWidth(),
                verticalArrangement = Arrangement.spacedBy(8.dp),
            ) {
                items(events, key = { "${it.uid}|${it.startEpochMilli}" }) { event ->
                    val focused = focusedEventUid?.let { needle ->
                        event.uid == needle || event.uid.endsWith(needle) || needle.endsWith(event.uid)
                    } == true
                    CalendarEventRow(
                        event = event,
                        highlighted = focused,
                        onClick = { onEventClick(event) },
                    )
                }
            }
        }
    }
}

@Composable
private fun rememberEventsByDay(events: List<CalendarEventItem>): Map<java.time.LocalDate, List<CalendarEventItem>> {
    return androidx.compose.runtime.remember(events) {
        val map = mutableMapOf<java.time.LocalDate, MutableList<CalendarEventItem>>()
        events.forEach { event ->
            val zone = java.time.ZoneId.systemDefault()
            val start = java.time.Instant.ofEpochMilli(event.startEpochMilli).atZone(zone).toLocalDate()
            val end = java.time.Instant.ofEpochMilli(event.endEpochMilli).atZone(zone).toLocalDate()
            var day = start
            while (!day.isAfter(end)) {
                if (CalendarRepository.eventOccursOnDay(event, day)) {
                    map.getOrPut(day) { mutableListOf() }.add(event)
                }
                day = day.plusDays(1)
                if (!event.allDay) break
            }
        }
        map
    }
}
