package de.storchp.fdroidbuildstatus

import android.content.Intent
import android.content.res.Configuration
import android.os.Bundle
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.net.toUri
import androidx.core.view.updatePadding
import dagger.hilt.android.AndroidEntryPoint
import de.storchp.fdroidbuildstatus.adapter.ApiResponse
import de.storchp.fdroidbuildstatus.adapter.db.DbAdapter
import de.storchp.fdroidbuildstatus.adapter.fdroid.FdroidClient
import de.storchp.fdroidbuildstatus.adapter.fdroid.PublishedVersions
import de.storchp.fdroidbuildstatus.adapter.gitlab.GitlabClient
import de.storchp.fdroidbuildstatus.adapter.gitlab.Metadata
import de.storchp.fdroidbuildstatus.databinding.ActivityDetailBinding
import de.storchp.fdroidbuildstatus.model.App
import de.storchp.fdroidbuildstatus.model.AppBuild
import de.storchp.fdroidbuildstatus.model.AppNotification
import de.storchp.fdroidbuildstatus.model.BuildCycle
import de.storchp.fdroidbuildstatus.model.MetadataLinkType
import de.storchp.fdroidbuildstatus.utils.FormatUtils
import de.storchp.fdroidbuildstatus.utils.PreferencesService
import de.storchp.fdroidbuildstatus.utils.addInsetsListener
import java.util.concurrent.atomic.AtomicInteger
import javax.inject.Inject

private val TAG = DetailActivity::class.java.simpleName
const val BUNDLE_PUBLISHED_VERSION = "PUBLISHED_VERSION"
const val BUNDLE_METADATA_VERSION = "METADATA_VERSION"

@AndroidEntryPoint
class DetailActivity : AppCompatActivity() {

    @Inject
    lateinit var preferencesService: PreferencesService

    @Inject
    lateinit var dbAdapter: DbAdapter

    @Inject
    lateinit var fdroidClient: FdroidClient

    @Inject
    lateinit var gitlabClient: GitlabClient

    private lateinit var binding: ActivityDetailBinding
    private lateinit var header: DetailsMetadataView

    private var app: App? = null
    private val runningFetches = AtomicInteger(0)
    private var publishedVersionText: String? = null
    private var metadataVersionText: String? = null
    private var detailAppBuildListAdapter: DetailAppBuildListAdapter? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        enableEdgeToEdge()
        super.onCreate(savedInstanceState)
        binding = ActivityDetailBinding.inflate(layoutInflater)
        binding.builds.addInsetsListener {
            updatePadding(bottom = it.bottom)
        }
        binding.topAppBar.addInsetsListener {
            updatePadding(left = it.left, right = it.right)
        }
        binding.content.addInsetsListener {
            updatePadding(left = it.left, right = it.right)
        }
        setSupportActionBar(binding.topAppBar)
        setContentView(binding.root)
        supportActionBar?.setDisplayHomeAsUpEnabled(true)

        val orientation = getResources().configuration.orientation
        if (orientation == Configuration.ORIENTATION_PORTRAIT) {
            header = DetailsMetadataView(this)
            binding.builds.addHeaderView(header)
        } else {
            header = findViewById(R.id.metadata)
        }
        header.addInsetsListener {
            updatePadding(bottom = it.bottom)
        }
        header.setFavouriteOnClickListener { toggleFavourite() }

        supportFragmentManager.setFragmentResultListener(
            METADATA_LINK_TYPE_REQUEST,
            this
        ) { _: String?, bundle: Bundle ->
            val metadataLinkType = MetadataLinkType.valueOf(
                bundle.getString(METADATA_LINK_TYPE_RESULT)!!
            )
            if (metadataLinkType.metadataBaseUri != null) {
                openMetadataLink(metadataLinkType)
            }
        }
    }

    override fun onSaveInstanceState(outState: Bundle) {
        outState.putString(BUNDLE_PUBLISHED_VERSION, publishedVersionText)
        outState.putString(BUNDLE_METADATA_VERSION, metadataVersionText)
        super.onSaveInstanceState(outState)
    }

    override fun onRestoreInstanceState(savedInstanceState: Bundle) {
        super.onRestoreInstanceState(savedInstanceState)
        publishedVersionText = savedInstanceState.getString(BUNDLE_PUBLISHED_VERSION)
        metadataVersionText = savedInstanceState.getString(BUNDLE_METADATA_VERSION)
    }

    override fun onResume() {
        super.onResume()
        val intent = intent
        if (intent != null) {
            val id = intent.getStringExtra(EXTRA_BUILD_ITEM_ID)
            if (id != null) {
                app = dbAdapter.loadAppBuilds(id)
                if (app == null) {
                    app = dbAdapter.loadApp(id)
                    if (app == null) {
                        app = App(id, getString(R.string.not_found_build_item_name))
                    }
                }
            }
            app?.let {
                header.setApp(it)
                detailAppBuildListAdapter =
                    DetailAppBuildListAdapter(this, it.appBuildsByVersionCodeAndStatus) { build ->
                        onBuildSelected(build)
                    }
                binding.builds.adapter = detailAppBuildListAdapter
                fetchPublishedVersions(it.id)
                fetchMetadata(it.id)
            }
        }
    }

    private fun fetchMetadata(id: String) {
        if (metadataVersionText != null) {
            header.setMetadataVersion(metadataVersionText)
            return
        }
        startProgress()
        gitlabClient.getFdroidDataMetadata(id) { response: ApiResponse<Metadata?> ->
            stopProgress()
            if (response.status() == ApiResponse.Status.SUCCESS) {
                val metadata = response.value()
                metadata!!.highestVersion?.let { (versionCode, versionName): Metadata.Build ->
                    metadataVersionText = getString(
                        R.string.metadata_version, FormatUtils.formatVersion(
                            versionCode, versionName
                        )
                    )
                    header.setMetadataVersion(metadataVersionText)
                }
                app!!.name = metadata.appName
                header.setAppName(app!!.name)
                app!!.sourceCode = metadata.sourceCode
                dbAdapter.updateApp(id, metadata)
                for (i in 0 until detailAppBuildListAdapter!!.count) {
                    detailAppBuildListAdapter!!.getItemForPosition(i)?.let { appBuild ->
                        if (appBuild.versionName == null) {
                            appBuild.versionName = metadata.builds
                                .filter { it.versionCode == appBuild.versionCode }
                                .map(Metadata.Build::versionName)
                                .firstOrNull()
                        }
                    }
                }
                detailAppBuildListAdapter!!.notifyDataSetChanged()
            } else if (response.status() == ApiResponse.Status.NOT_FOUND) {
                metadataVersionText = getString(R.string.metadata_version_none)
                header.setMetadataVersion(metadataVersionText)
            } else {
                Toast.makeText(
                    this@DetailActivity,
                    resources.getString(
                        R.string.loading_metadata_versions_failed,
                        response.errorMessage()
                    ),
                    Toast.LENGTH_LONG
                ).show()
            }
        }
    }

    private fun onBuildSelected(selectedAppBuild: AppBuild?) {
        if (selectedAppBuild?.buildCycle?.isUpdatable == true) {
            startActivity(
                Intent(this, BuildlogActivity::class.java)
                    .putExtra(EXTRA_BUILD_ITEM_ID, app!!.id)
                    .putExtra(
                        EXTRA_BUILD_ITEM_VERSION_CODE,
                        selectedAppBuild.versionCode
                    )
            )
        }
    }

    private fun fetchPublishedVersions(id: String) {
        if (publishedVersionText != null) {
            header.setPublishedVersions(publishedVersionText)
            return
        }
        startProgress()
        fdroidClient.getPublishedVersions(id) { response: ApiResponse<PublishedVersions?> ->
            stopProgress()
            if (response.isSuccess) {
                val publishedVersions = response.value()
                publishedVersionText = resources.getString(R.string.published_versions) +
                        publishedVersions!!.packages.joinToString(
                            "\n· ",
                            "\n· "
                        ) { (versionName, versionCode): PublishedVersions.PublishedPackage ->
                            FormatUtils.formatVersion(versionCode, versionName)
                        }
                header.setPublishedVersions(publishedVersionText)
                dbAdapter.saveNotification(
                    BuildCycle.PUBLISH,
                    AppNotification(id, publishedVersions.packages.maxOf { it.versionCode })
                )
            } else if (response.status() == ApiResponse.Status.NOT_FOUND) {
                publishedVersionText = getString(R.string.published_versions_none)
                header.setPublishedVersions(publishedVersionText)
            } else {
                Toast.makeText(
                    this@DetailActivity,
                    resources.getString(
                        R.string.loading_published_versions_failed,
                        response.errorMessage()
                    ),
                    Toast.LENGTH_LONG
                ).show()
            }
        }
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        menuInflater.inflate(R.menu.menu_detail, menu)
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when (item.itemId) {
            R.id.action_open -> {
                openUrl(app!!.fdroidUri)
                return true
            }

            R.id.action_metadata -> {
                val metadataLinkType = preferencesService.getMetadataLinkType()
                if (metadataLinkType.metadataBaseUri != null) {
                    openMetadataLink(metadataLinkType)
                } else {
                    val metadataLinkTypeFragment = MetadataLinkTypeFragment()
                    metadataLinkTypeFragment.show(
                        supportFragmentManager,
                        METADATA_LINK_TYPE_REQUEST
                    )
                }
                return true
            }

            R.id.action_source -> {
                val sourceCodeUrl = app!!.sourceCode
                try {
                    openUrl(sourceCodeUrl!!)
                    return true
                } catch (_: Exception) {
                    Log.e(TAG, "Failed to start activity for SourceCode: $sourceCodeUrl")
                }
            }
        }
        return super.onOptionsItemSelected(item)
    }

    private fun openMetadataLink(metadataLinkType: MetadataLinkType) {
        openUrl(app!!.getMetadataUri(metadataLinkType), metadataLinkType.mimeType)
    }

    private fun openUrl(uriString: String, type: String? = null) {
        val intent = Intent(Intent.ACTION_VIEW)
        val uri = uriString.toUri()
        if (type != null) {
            intent.setDataAndType(uri, type)
        } else {
            intent.setData(uri)
        }
        startActivity(intent)
    }

    private fun toggleFavourite() {
        app!!.favourite = !app!!.favourite
        dbAdapter.toggleFavourite(app!!.id)
        header.setFavoriteIcon(app!!)
    }

    private fun startProgress() {
        runningFetches.incrementAndGet()
        binding.progressBar.visibility = View.VISIBLE
    }

    private fun stopProgress() {
        if (runningFetches.decrementAndGet() == 0) {
            binding.progressBar.visibility = View.GONE
        }
    }

}