package eu.kanade.tachiyomi.ui.setting

import android.annotation.SuppressLint
import android.app.Dialog
import android.content.ActivityNotFoundException
import android.content.Intent
import android.os.Bundle
import android.provider.Settings
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.core.net.toUri
import androidx.core.text.HtmlCompat
import androidx.preference.PreferenceScreen
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.cache.ChapterCache
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
import eu.kanade.tachiyomi.data.library.LibraryUpdateService.Target
import eu.kanade.tachiyomi.data.preference.PreferenceValues
import eu.kanade.tachiyomi.data.preference.asImmediateFlow
import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.network.PREF_DOH_ADGUARD
import eu.kanade.tachiyomi.network.PREF_DOH_CLOUDFLARE
import eu.kanade.tachiyomi.network.PREF_DOH_GOOGLE
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.SourceManager.Companion.DELEGATED_SOURCES
import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.ui.base.controller.openInBrowser
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.util.CrashLogUtil
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.withUIContext
import eu.kanade.tachiyomi.util.preference.defaultValue
import eu.kanade.tachiyomi.util.preference.editTextPreference
import eu.kanade.tachiyomi.util.preference.entriesRes
import eu.kanade.tachiyomi.util.preference.intListPreference
import eu.kanade.tachiyomi.util.preference.listPreference
import eu.kanade.tachiyomi.util.preference.onChange
import eu.kanade.tachiyomi.util.preference.onClick
import eu.kanade.tachiyomi.util.preference.preference
import eu.kanade.tachiyomi.util.preference.preferenceCategory
import eu.kanade.tachiyomi.util.preference.summaryRes
import eu.kanade.tachiyomi.util.preference.switchPreference
import eu.kanade.tachiyomi.util.preference.titleRes
import eu.kanade.tachiyomi.util.storage.DiskUtil
import eu.kanade.tachiyomi.util.system.MiuiUtil
import eu.kanade.tachiyomi.util.system.isPackageInstalled
import eu.kanade.tachiyomi.util.system.isTablet
import eu.kanade.tachiyomi.util.system.powerManager
import eu.kanade.tachiyomi.util.system.toast
import exh.debug.SettingsDebugController
import exh.log.EHLogLevel
import exh.source.BlacklistedSources
import exh.source.EH_SOURCE_ID
import exh.source.EXH_SOURCE_ID
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.launchIn
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys

class SettingsAdvancedController : SettingsController() {
    private val network: NetworkHelper by injectLazy()

    private val chapterCache: ChapterCache by injectLazy()

    private val db: DatabaseHelper by injectLazy()

    @SuppressLint("BatteryLife")
    override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
        titleRes = R.string.pref_category_advanced

        preference {
            key = "dump_crash_logs"
            titleRes = R.string.pref_dump_crash_logs
            summaryRes = R.string.pref_dump_crash_logs_summary

            onClick {
                CrashLogUtil(context).dumpLogs()
            }
        }

        /*switchPreference {
            key = Keys.verboseLogging
            titleRes = R.string.pref_verbose_logging
            summaryRes = R.string.pref_verbose_logging_summary
            defaultValue = false

            onChange {
                activity?.toast(R.string.requires_app_restart)
                true
            }
        }*/

        preferenceCategory {
            titleRes = R.string.label_background_activity

            preference {
                key = "pref_disable_battery_optimization"
                titleRes = R.string.pref_disable_battery_optimization
                summaryRes = R.string.pref_disable_battery_optimization_summary

                onClick {
                    val packageName: String = context.packageName
                    if (!context.powerManager.isIgnoringBatteryOptimizations(packageName)) {
                        try {
                            val intent = Intent().apply {
                                action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
                                data = "package:$packageName".toUri()
                            }
                            startActivity(intent)
                        } catch (e: ActivityNotFoundException) {
                            context.toast(R.string.battery_optimization_setting_activity_not_found)
                        }
                    } else {
                        context.toast(R.string.battery_optimization_disabled)
                    }
                }
            }

            preference {
                key = "pref_dont_kill_my_app"
                title = "Don't kill my app!"
                summaryRes = R.string.about_dont_kill_my_app

                onClick {
                    openInBrowser("https://dontkillmyapp.com/")
                }
            }
        }

        preferenceCategory {
            titleRes = R.string.label_data

            preference {
                key = CLEAR_CACHE_KEY
                titleRes = R.string.pref_clear_chapter_cache
                summary = context.getString(R.string.used_cache, chapterCache.readableSize)

                onClick { clearChapterCache() }
            }
            preference {
                key = "pref_clear_database"
                titleRes = R.string.pref_clear_database
                summaryRes = R.string.pref_clear_database_summary

                onClick {
                    val ctrl = ClearDatabaseDialogController()
                    ctrl.targetController = this@SettingsAdvancedController
                    ctrl.showDialog(router)
                }
            }
        }

        preferenceCategory {
            titleRes = R.string.label_network

            preference {
                key = "pref_clear_cookies"
                titleRes = R.string.pref_clear_cookies

                onClick {
                    network.cookieManager.removeAll()
                    activity?.toast(R.string.cookies_cleared)
                }
            }
            intListPreference {
                key = Keys.dohProvider
                titleRes = R.string.pref_dns_over_https
                entries = arrayOf(
                    context.getString(R.string.disabled),
                    "Cloudflare",
                    "Google",
                    "AdGuard",
                )
                entryValues = arrayOf(
                    "-1",
                    PREF_DOH_CLOUDFLARE.toString(),
                    PREF_DOH_GOOGLE.toString(),
                    PREF_DOH_ADGUARD.toString(),
                )
                defaultValue = "-1"
                summary = "%s"

                onChange {
                    activity?.toast(R.string.requires_app_restart)
                    true
                }
            }
        }

        preferenceCategory {
            titleRes = R.string.label_library

            preference {
                key = "pref_refresh_library_covers"
                titleRes = R.string.pref_refresh_library_covers

                onClick { LibraryUpdateService.start(context, target = Target.COVERS) }
            }
            preference {
                key = "pref_refresh_library_tracking"
                titleRes = R.string.pref_refresh_library_tracking
                summaryRes = R.string.pref_refresh_library_tracking_summary

                onClick { LibraryUpdateService.start(context, target = Target.TRACKING) }
            }
        }

        preferenceCategory {
            titleRes = R.string.label_extensions

            listPreference {
                key = Keys.extensionInstaller
                titleRes = R.string.ext_installer_pref
                summary = "%s"
                entriesRes = arrayOf(
                    R.string.ext_installer_legacy,
                    R.string.ext_installer_packageinstaller,
                    R.string.ext_installer_shizuku,
                )
                entryValues = PreferenceValues.ExtensionInstaller.values().map { it.name }.toTypedArray()
                defaultValue = if (MiuiUtil.isMiui()) {
                    PreferenceValues.ExtensionInstaller.LEGACY
                } else {
                    PreferenceValues.ExtensionInstaller.PACKAGEINSTALLER
                }.name

                onChange {
                    if (it == PreferenceValues.ExtensionInstaller.SHIZUKU.name &&
                        !context.isPackageInstalled("moe.shizuku.privileged.api")
                    ) {
                        MaterialAlertDialogBuilder(context)
                            .setTitle(R.string.ext_installer_shizuku)
                            .setMessage(R.string.ext_installer_shizuku_unavailable_dialog)
                            .setPositiveButton(android.R.string.ok) { _, _ ->
                                openInBrowser("https://shizuku.rikka.app/download")
                            }
                            .setNegativeButton(android.R.string.cancel, null)
                            .show()
                        false
                    } else {
                        true
                    }
                }
            }
        }

        preferenceCategory {
            titleRes = R.string.pref_category_display

            listPreference {
                key = Keys.tabletUiMode
                titleRes = R.string.pref_tablet_ui_mode
                summary = "%s"
                entriesRes = arrayOf(R.string.lock_always, R.string.landscape, R.string.lock_never)
                entryValues = PreferenceValues.TabletUiMode.values().map { it.name }.toTypedArray()
                defaultValue = if (context.isTablet()) {
                    PreferenceValues.TabletUiMode.ALWAYS
                } else {
                    PreferenceValues.TabletUiMode.NEVER
                }.name

                onChange {
                    activity?.toast(R.string.requires_app_restart)
                    true
                }
            }
        }

        // --> EXH
        preferenceCategory {
            titleRes = R.string.download_notifier_downloader_title

            preference {
                key = "clean_up_downloaded_chapters"
                titleRes = R.string.clean_up_downloaded_chapters
                summaryRes = R.string.delete_unused_chapters

                onClick {
                    val ctrl = CleanupDownloadsDialogController()
                    ctrl.targetController = this@SettingsAdvancedController
                    ctrl.showDialog(router)
                }
            }
        }

        preferenceCategory {
            titleRes = R.string.data_saver

            switchPreference {
                titleRes = R.string.data_saver
                summaryRes = R.string.data_saver_summary
                key = Keys.dataSaver
                defaultValue = false
            }

            editTextPreference {
                titleRes = R.string.data_saver_server
                key = Keys.dataSaverServer
                defaultValue = ""
                summaryRes = R.string.data_saver_server_summary

                preferences.dataSaver().asImmediateFlow { isVisible = it }
                    .launchIn(viewScope)
            }

            switchPreference {
                titleRes = R.string.ignore_jpeg
                key = Keys.ignoreJpeg
                defaultValue = false

                preferences.dataSaver().asImmediateFlow { isVisible = it }
                    .launchIn(viewScope)
            }

            switchPreference {
                titleRes = R.string.ignore_gif
                key = Keys.ignoreGif
                defaultValue = true

                preferences.dataSaver().asImmediateFlow { isVisible = it }
                    .launchIn(viewScope)
            }

            intListPreference {
                titleRes = R.string.data_saver_image_quality
                key = Keys.dataSaverImageQuality
                entries = arrayOf("10", "20", "40", "50", "70", "80", "90", "95")
                entryValues = entries
                defaultValue = "80"
                summaryRes = R.string.data_saver_image_quality_summary

                preferences.dataSaver().asImmediateFlow { isVisible = it }
                    .launchIn(viewScope)
            }

            switchPreference {
                titleRes = R.string.data_saver_image_format
                key = Keys.dataSaverImageFormatJpeg
                defaultValue = false
                summaryOn = context.getString(R.string.data_saver_image_format_summary_on)
                summaryOff = context.getString(R.string.data_saver_image_format_summary_off)

                preferences.dataSaver().asImmediateFlow { isVisible = it }
                    .launchIn(viewScope)
            }

            switchPreference {
                titleRes = R.string.data_saver_color_bw
                key = Keys.dataSaverColorBW
                defaultValue = false

                preferences.dataSaver().asImmediateFlow { isVisible = it }
                    .launchIn(viewScope)
            }
        }

        preferenceCategory {
            titleRes = R.string.developer_tools
            isPersistent = false

            switchPreference {
                titleRes = R.string.toggle_hentai_features
                summaryRes = R.string.toggle_hentai_features_summary
                key = Keys.eh_is_hentai_enabled
                defaultValue = true

                onChange {
                    if (preferences.isHentaiEnabled().get()) {
                        BlacklistedSources.HIDDEN_SOURCES += EH_SOURCE_ID
                        BlacklistedSources.HIDDEN_SOURCES += EXH_SOURCE_ID
                    } else {
                        BlacklistedSources.HIDDEN_SOURCES -= EH_SOURCE_ID
                        BlacklistedSources.HIDDEN_SOURCES -= EXH_SOURCE_ID
                    }
                    true
                }
            }

            switchPreference {
                titleRes = R.string.toggle_delegated_sources
                key = Keys.eh_delegateSources
                defaultValue = true
                summary = context.getString(R.string.toggle_delegated_sources_summary, context.getString(R.string.app_name), DELEGATED_SOURCES.values.map { it.sourceName }.distinct().joinToString())
            }

            intListPreference {
                key = Keys.eh_logLevel
                titleRes = R.string.log_level

                entries = EHLogLevel.values().map {
                    "${context.getString(it.nameRes)} (${context.getString(it.description)})"
                }.toTypedArray()
                entryValues = EHLogLevel.values().mapIndexed { index, _ -> "$index" }.toTypedArray()
                defaultValue = "0"

                summaryRes = R.string.log_level_summary
            }

            switchPreference {
                titleRes = R.string.enable_source_blacklist
                key = Keys.eh_enableSourceBlacklist
                defaultValue = true
                summary = context.getString(R.string.enable_source_blacklist_summary, context.getString(R.string.app_name))
            }

            preference {
                key = "pref_open_debug_menu"
                titleRes = R.string.open_debug_menu
                summary = HtmlCompat.fromHtml(context.getString(R.string.open_debug_menu_summary), HtmlCompat.FROM_HTML_MODE_LEGACY)
                onClick { router.pushController(SettingsDebugController().withFadeTransaction()) }
            }
        }
        // <-- EXH
    }

    // SY -->
    class CleanupDownloadsDialogController : DialogController() {
        override fun onCreateDialog(savedViewState: Bundle?): Dialog {
            val options = activity!!.resources.getStringArray(R.array.clean_up_downloads)
            val selected = options.map { true }.toBooleanArray()
            return MaterialAlertDialogBuilder(activity!!)
                .setTitle(R.string.clean_up_downloaded_chapters)
                .setMultiChoiceItems(options, selected) { dialog, which, checked ->
                    if (which == 0) {
                        (dialog as AlertDialog).listView.setItemChecked(which, true)
                    } else {
                        selected[which] = checked
                    }
                }
                .setPositiveButton(android.R.string.ok) { _, _ ->
                    (targetController as? SettingsAdvancedController)?.cleanupDownloads(
                        selected[1],
                        selected[2]
                    )
                }
                .setNegativeButton(android.R.string.cancel, null)
                .create()
        }
    }

    private fun cleanupDownloads(removeRead: Boolean, removeNonFavorite: Boolean) {
        if (job?.isActive == true) return
        activity?.toast(R.string.starting_cleanup)
        job = launchIO {
            val mangaList = db.getMangas().executeAsBlocking()
            val downloadManager: DownloadManager = Injekt.get()
            var foldersCleared = 0
            Injekt.get<SourceManager>().getOnlineSources().forEach { source ->
                val mangaFolders = downloadManager.getMangaFolders(source)
                val sourceManga = mangaList
                    .asSequence()
                    .filter { it.source == source.id }
                    .map { it to DiskUtil.buildValidFilename(it.originalTitle) }
                    .toList()

                mangaFolders.forEach mangaFolder@{ mangaFolder ->
                    val manga = sourceManga.find { (_, folderName) -> folderName == mangaFolder.name }?.first
                    if (manga == null) {
                        // download is orphaned delete it
                        foldersCleared += 1 + (mangaFolder.listFiles().orEmpty().size)
                        mangaFolder.delete()
                    } else {
                        val chapterList = db.getChapters(manga).executeAsBlocking()
                        foldersCleared += downloadManager.cleanupChapters(chapterList, manga, source, removeRead, removeNonFavorite)
                    }
                }
            }
            withUIContext {
                val activity = activity ?: return@withUIContext
                val cleanupString =
                    if (foldersCleared == 0) activity.getString(R.string.no_folders_to_cleanup)
                    else resources!!.getQuantityString(
                        R.plurals.cleanup_done,
                        foldersCleared,
                        foldersCleared
                    )
                activity.toast(cleanupString, Toast.LENGTH_LONG)
            }
        }
    }
    // SY <--

    private fun clearChapterCache() {
        if (activity == null) return
        launchIO {
            try {
                val deletedFiles = chapterCache.clear()
                withUIContext {
                    activity?.toast(resources?.getString(R.string.cache_deleted, deletedFiles))
                    findPreference(CLEAR_CACHE_KEY)?.summary =
                        resources?.getString(R.string.used_cache, chapterCache.readableSize)
                }
            } catch (e: Throwable) {
                withUIContext { activity?.toast(R.string.cache_delete_error) }
            }
        }
    }

    class ClearDatabaseDialogController : DialogController() {
        override fun onCreateDialog(savedViewState: Bundle?): Dialog {
            val item = arrayOf(
                activity!!.getString(R.string.clear_database_confirmation),
                activity!!.getString(R.string.clear_db_exclude_read)
            )
            val selected = booleanArrayOf(true, true)
            return MaterialAlertDialogBuilder(activity!!)
                // .setMessage(R.string.clear_database_confirmation)
                // SY -->
                .setMultiChoiceItems(item, selected) { _, which, checked ->
                    if (which == 0) {
                        (dialog as AlertDialog).listView.setItemChecked(which, true)
                    } else {
                        selected[which] = checked
                    }
                }
                // SY <--
                .setPositiveButton(android.R.string.ok) { _, _ ->
                    (targetController as? SettingsAdvancedController)?.clearDatabase(selected.last())
                }
                .setNegativeButton(android.R.string.cancel, null)
                .create()
        }
    }

    private fun clearDatabase(keepReadManga: Boolean) {
        // SY -->
        if (keepReadManga) {
            db.deleteMangasNotInLibraryAndNotRead().executeAsBlocking()
        } else {
            db.deleteMangasNotInLibrary().executeAsBlocking()
        }
        // SY <--
        db.deleteHistoryNoLastRead().executeAsBlocking()
        activity?.toast(R.string.clear_database_completed)
    }

    private companion object {
        // SY -->
        private var job: Job? = null
        // SY <--
    }
}

private const val CLEAR_CACHE_KEY = "pref_clear_cache_key"
