package net.sergeych.toread

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

@Composable
@Preview
fun App() {
    var themeMode by remember { mutableStateOf(ThemeMode.SYSTEM) }
    var systemDark by remember { mutableStateOf(isPlatformDarkTheme()) }
    var toastMessage by remember { mutableStateOf<String?>(null) }
    val scope = rememberCoroutineScope()
    val useDark = when (themeMode) {
        ThemeMode.LIGHT -> false
        ThemeMode.DARK -> true
        ThemeMode.SYSTEM -> systemDark
    }

    DisposableEffect(Unit) {
        val dispose = watchPlatformDarkTheme { systemDark = it }
        onDispose(dispose)
    }

    LaunchedEffect(Unit) {
        themeMode = loadThemeMode()
    }

    LaunchedEffect(toastMessage) {
        if (toastMessage != null) {
            delay(1600)
            toastMessage = null
        }
    }

    MaterialTheme(colorScheme = if (useDark) darkReaderColorScheme() else lightReaderColorScheme()) {
        Box(Modifier.fillMaxSize()) {
            Surface(Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
                BookReaderApp(
                    onThemeToggle = {
                        val next = themeMode.next()
                        themeMode = next
                        toastMessage = "Theme: ${next.displayName}"
                        scope.launch {
                            saveThemeMode(next)
                        }
                    },
                )
            }
            AppToast(toastMessage, modifier = Modifier.align(Alignment.BottomCenter))
        }
    }
}

@Composable
private fun AppToast(message: String?, modifier: Modifier = Modifier) {
    if (message != null) {
        Surface(
            modifier = modifier.padding(bottom = 24.dp),
            shape = RoundedCornerShape(8.dp),
            color = MaterialTheme.colorScheme.inverseSurface,
            tonalElevation = 6.dp,
        ) {
            Text(
                message,
                style = MaterialTheme.typography.bodyMedium,
                color = MaterialTheme.colorScheme.inverseOnSurface,
                modifier = Modifier.padding(horizontal = 16.dp, vertical = 10.dp),
            )
        }
    }
}

@Composable
private fun BookReaderApp(onThemeToggle: () -> Unit) {
    var state by remember { mutableStateOf<AppState>(AppState.LoadingLibrary) }
    var activeScan by remember { mutableStateOf<LibraryScanProgress?>(null) }
    var scanJob by remember { mutableStateOf<Job?>(null) }
    var imageViewer by remember { mutableStateOf<ViewedBookImage?>(null) }
    val scope = rememberCoroutineScope()

    LaunchedEffect(Unit) {
        state = loadStartupState()
    }

    fun startScan(path: String) {
        if (scanJob?.isActive == true) return
        activeScan = LibraryScanProgress(0, 0, 0, 0)
        scanJob = scope.launch {
            val report = runCatching {
                scanLibrarySubtree(path) { progress ->
                    scope.launch {
                        if (scanJob?.isActive == true) activeScan = progress
                    }
                }
            }
            val message = report.fold(
                onSuccess = {
                    if (isDownloadsScanPath(path)) {
                        saveDownloadsWasScanned(true)
                    }
                    "Scanned ${it.scannedFiles}, imported ${it.importedFiles}, skipped ${it.skippedFiles}, failed ${it.failedFiles}."
                },
                onFailure = { it.message ?: "Scan failed." },
            )
            scanJob = null
            activeScan = null
            state = when (val current = state) {
                is AppState.Library, is AppState.Scan, AppState.LoadingLibrary -> loadLibraryState(message, path)
                is AppState.Reader -> current.copy(message = message)
                is AppState.BookInfo -> current.copy(message = message)
                is AppState.Error -> current
            }
        }
    }

    when (val current = state) {
        AppState.LoadingLibrary -> LoadingScreen("Opening library")
        is AppState.Library -> LibraryScreen(
            state = current,
            activeScan = activeScan,
            onStateChange = { state = it },
            onNavigateToScan = { state = AppState.Scan(current.items, current.scanPath, current.message) },
            onStartScan = ::startScan,
        )
        is AppState.Scan -> ScanScreen(
            state = current,
            activeScan = activeScan,
            onStateChange = { state = it },
            onStartScan = { path ->
                startScan(path)
                state = AppState.Library(current.items, path, "Scanning...")
            },
        )
        is AppState.Reader -> BookView(
            fileId = current.fileId,
            book = current.book,
            onImageOpen = { imageViewer = it },
            onThemeToggle = onThemeToggle,
            onBookInfo = {
                state = AppState.BookInfo(
                    fileId = current.fileId,
                    book = current.book,
                    libraryItems = current.libraryItems,
                    scanPath = current.scanPath,
                    message = current.message,
                )
            },
            onBack = {
                state = AppState.Library(emptyList(), current.scanPath, current.message)
            },
        )
        is AppState.BookInfo -> BookInfoScreen(
            fileId = current.fileId,
            book = current.book,
            onImageOpen = { imageViewer = it },
            onBack = {
                state = AppState.Reader(
                    fileId = current.fileId,
                    book = current.book,
                    libraryItems = current.libraryItems,
                    scanPath = current.scanPath,
                    message = current.message,
                )
            },
        )
        is AppState.Error -> ErrorScreen(current.message, onBack = { state = AppState.LoadingLibrary })
    }

    imageViewer?.let { image ->
        ImageViewer(
            image = image,
            onBack = { imageViewer = null },
        )
    }
}

internal data class ViewedBookImage(
    val bitmap: ImageBitmap,
    val bytes: ByteArray,
    val mimeType: String,
    val title: String,
)
