package app.k9mail.feature.settings.import.ui

import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.os.bundleOf
import androidx.core.view.isGone
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.setFragmentResult
import androidx.fragment.app.setFragmentResultListener
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.RecyclerView
import app.k9mail.feature.migration.launcher.api.MigrationManager
import app.k9mail.feature.settings.importing.R
import com.fsck.k9.ui.base.livedata.observeNotNull
import com.google.android.material.button.MaterialButton
import com.google.android.material.textview.MaterialTextView
import com.mikepenz.fastadapter.FastAdapter
import com.mikepenz.fastadapter.adapters.ItemAdapter
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
import com.fsck.k9.ui.base.R as BaseR

class SettingsImportFragment : Fragment() {
    private val viewModel: SettingsImportViewModel by viewModel()
    private val migrationManager: MigrationManager by inject()

    private lateinit var settingsImportAdapter: FastAdapter<ImportListItem<*>>
    private lateinit var itemAdapter: ItemAdapter<ImportListItem<*>>

    private val qrCodeScannerResultContract = registerForActivityResult(
        migrationManager.getQrCodeActivityResultContract(),
    ) { contentUri ->
        if (contentUri != null) {
            viewModel.onDocumentPicked(contentUri)
        } else {
            viewModel.onDocumentPickCanceled()
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        viewModel.initialize()

        setFragmentResultListener(PickAppDialogFragment.FRAGMENT_RESULT_KEY) { _, result: Bundle ->
            val packageName = result.getString(PickAppDialogFragment.FRAGMENT_RESULT_APP)
            if (packageName != null) {
                viewModel.onAppPicked(packageName)
            } else {
                viewModel.onAppPickCanceled()
            }
        }
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_settings_import, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        if (savedInstanceState != null) {
            viewModel.initializeFromSavedState(savedInstanceState)
        }

        val viewHolder = ViewHolder(view)

        initializeSettingsImportList(viewHolder.settingsImportList)
        viewHolder.pickDocumentButton.setOnClickListener { viewModel.onPickDocumentButtonClicked() }
        viewHolder.scanQrCodeButton.setOnClickListener { viewModel.onScanQrCodeButtonClicked() }
        viewHolder.pickAppButton.setOnClickListener { viewModel.onPickAppButtonClicked() }
        viewHolder.importButton.setOnClickListener { viewModel.onImportButtonClicked() }
        viewHolder.closeButton.setOnClickListener { viewModel.onCloseButtonClicked() }

        viewModel.getUiModel().observeNotNull(this) { viewHolder.updateUi(it) }
        viewModel.getActionEvents().observeNotNull(this) { handleActionEvents(it) }

        arguments?.let { arguments ->
            val action = SettingsImportAction.fromBundle(arguments)
            viewModel.setAction(action)
        }
    }

    private fun initializeSettingsImportList(recyclerView: RecyclerView) {
        itemAdapter = ItemAdapter()
        settingsImportAdapter = FastAdapter.with(itemAdapter).apply {
            setHasStableIds(true)
            onClickListener = { _, _, _, position ->
                viewModel.onSettingsListItemClicked(position)
                true
            }
            addEventHook(
                ImportListItemClickEvent { position ->
                    viewModel.onSettingsListItemClicked(position)
                },
            )
        }

        recyclerView.adapter = settingsImportAdapter
    }

    private fun ViewHolder.updateUi(model: SettingsImportUiModel) {
        when (model.importButton) {
            ButtonState.DISABLED -> {
                importButton.isVisible = true
                importButton.isEnabled = false
            }

            ButtonState.ENABLED -> {
                importButton.isVisible = true
                importButton.isEnabled = true
            }

            ButtonState.INVISIBLE -> importButton.isInvisible = true
            ButtonState.GONE -> importButton.isGone = true
        }

        closeButton.isGone = model.closeButton == ButtonState.GONE
        when (model.closeButtonLabel) {
            CloseButtonLabel.OK -> closeButton.setText(BaseR.string.okay_action)
            CloseButtonLabel.LATER -> closeButton.setText(R.string.settings_import_later_button)
        }

        settingsImportList.isVisible = model.isSettingsListVisible
        pickDocumentButton.isInvisible = !model.isPickDocumentButtonVisible
        pickDocumentButton.isEnabled = model.isPickDocumentButtonEnabled
        scanQrCodeButton.isInvisible = !model.isScanQrCodeButtonVisible
        scanQrCodeButton.isEnabled = model.isScanQrCodeButtonEnabled
        pickAppButton.isInvisible = !model.isPickAppButtonVisible
        pickAppButton.isEnabled = model.isPickAppButtonEnabled
        loadingProgressBar.isVisible = model.isLoadingProgressVisible
        importProgressBar.isVisible = model.isImportProgressVisible

        statusText.isVisible = model.statusText != StatusText.HIDDEN
        when (model.statusText) {
            StatusText.IMPORTING_PROGRESS -> {
                statusText.text = getString(R.string.settings_importing)
            }

            StatusText.IMPORT_SUCCESS -> {
                statusText.text = getString(R.string.settings_import_success_generic)
            }

            StatusText.IMPORT_SUCCESS_PASSWORD_REQUIRED -> {
                statusText.text = getString(R.string.settings_import_password_required)
            }

            StatusText.IMPORT_SUCCESS_AUTHORIZATION_REQUIRED -> {
                statusText.text = getString(R.string.settings_import_authorization_required)
            }

            StatusText.IMPORT_SUCCESS_PASSWORD_AND_AUTHORIZATION_REQUIRED -> {
                statusText.text = getString(R.string.settings_import_authorization_and_password_required)
            }

            StatusText.IMPORT_READ_FAILURE -> {
                statusText.text = getString(R.string.settings_import_read_failure)
            }

            StatusText.IMPORT_PARTIAL_FAILURE -> {
                statusText.text = getString(R.string.settings_import_partial_failure)
            }

            StatusText.IMPORT_FAILURE -> {
                statusText.text = getString(R.string.settings_import_failure)
            }

            StatusText.HIDDEN -> statusText.text = null
        }

        setSettingsList(model.settingsList, model.isSettingsListEnabled)
    }

    // TODO: Update list instead of replacing it completely
    private fun ViewHolder.setSettingsList(items: List<SettingsListItem>, enable: Boolean) {
        val importListItems = items.map { item ->
            val checkBoxItem = when (item) {
                is SettingsListItem.GeneralSettings -> GeneralSettingsItem(item.importStatus)
                is SettingsListItem.Account -> AccountItem(item)
            }

            checkBoxItem.apply {
                isSelected = item.selected
                isEnabled = item.enabled && enable
            }
        }

        itemAdapter.set(importListItems)

        settingsImportList.isEnabled = enable
    }

    private fun handleActionEvents(action: Action) {
        when (action) {
            is Action.Close -> closeImportScreen(action)
            is Action.PickDocument -> pickDocument()
            is Action.ScanQrCode -> scanQrCode()
            is Action.PickApp -> pickApp()
            is Action.PasswordPrompt -> showPasswordPrompt(action)
            is Action.StartAuthorization -> startAuthorization(action)
        }
    }

    @Suppress("SwallowedException")
    private fun closeImportScreen(action: Action.Close) {
        setFragmentResult(action.importSuccess)

        try {
            findNavController().popBackStack()
        } catch (e: IllegalStateException) {
            // Fragment does not have NavController
        }
    }

    private fun pickDocument() {
        val createDocumentIntent = Intent(Intent.ACTION_GET_CONTENT).apply {
            type = "*/*"
            addCategory(Intent.CATEGORY_OPENABLE)
        }
        startActivityForResult(createDocumentIntent, REQUEST_PICK_DOCUMENT)
    }

    private fun scanQrCode() {
        qrCodeScannerResultContract.launch(Unit)
    }

    private fun pickApp() {
        PickAppDialogFragment().show(parentFragmentManager, "pick_app")
    }

    private fun startAuthorization(action: Action.StartAuthorization) {
        val intent = OAuthFlowActivity.buildLaunchIntent(
            context = requireContext(),
            accountUuid = action.accountUuid,
        )

        startActivityForResult(intent, REQUEST_AUTHORIZATION)
    }

    private fun showPasswordPrompt(action: Action.PasswordPrompt) {
        val dialogFragment = PasswordPromptDialogFragment.create(
            action.accountUuid,
            action.accountName,
            action.inputIncomingServerPassword,
            action.incomingServerName,
            action.inputOutgoingServerPassword,
            action.outgoingServerName,
            targetFragment = this,
            requestCode = REQUEST_PASSWORD_PROMPT,
        )
        dialogFragment.show(requireFragmentManager(), null)
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        viewModel.saveInstanceState(outState)
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        when (requestCode) {
            REQUEST_PICK_DOCUMENT -> handlePickDocumentResult(resultCode, data)
            REQUEST_PASSWORD_PROMPT -> handlePasswordPromptResult(resultCode, data)
            REQUEST_AUTHORIZATION -> handleAuthorizationResult(resultCode)
        }
    }

    private fun handlePickDocumentResult(resultCode: Int, data: Intent?) {
        val contentUri = data?.data
        if (resultCode == Activity.RESULT_OK && contentUri != null) {
            viewModel.onDocumentPicked(contentUri)
        } else {
            viewModel.onDocumentPickCanceled()
        }
    }

    private fun handlePasswordPromptResult(resultCode: Int, data: Intent?) {
        if (resultCode == Activity.RESULT_OK) {
            val resultIntent = data ?: error("No result intent received")
            val result = PasswordPromptResult.fromIntent(resultIntent)
            viewModel.onPasswordPromptResult(result)
        }
    }

    private fun handleAuthorizationResult(resultCode: Int) {
        if (resultCode == Activity.RESULT_OK) {
            viewModel.onReturnAfterAuthorization()
        }
    }

    private fun setFragmentResult(accountImported: Boolean) {
        setFragmentResult(
            requestKey = FRAGMENT_RESULT_KEY,
            result = bundleOf(
                FRAGMENT_RESULT_ACCOUNT_IMPORTED to accountImported,
            ),
        )
    }

    companion object {
        private const val REQUEST_PICK_DOCUMENT = Activity.RESULT_FIRST_USER
        private const val REQUEST_PASSWORD_PROMPT = Activity.RESULT_FIRST_USER + 1
        private const val REQUEST_AUTHORIZATION = Activity.RESULT_FIRST_USER + 2

        const val FRAGMENT_RESULT_KEY = "settings_import"
        const val FRAGMENT_RESULT_ACCOUNT_IMPORTED = "accountImported"
    }
}

private class ViewHolder(view: View) {
    val pickDocumentButton: View = view.findViewById(R.id.pickDocumentButton)
    val scanQrCodeButton: View = view.findViewById(R.id.scanQrCodeButton)
    val pickAppButton: View = view.findViewById(R.id.pickAppButton)
    val importButton: View = view.findViewById(R.id.importButton)
    val closeButton: MaterialButton = view.findViewById(R.id.closeButton)
    val loadingProgressBar: View = view.findViewById(R.id.loadingProgressBar)
    val importProgressBar: View = view.findViewById(R.id.importProgressBar)
    val statusText: MaterialTextView = view.findViewById(R.id.statusText)
    val settingsImportList: RecyclerView = view.findViewById(R.id.settingsImportList)
}
