package ani.dantotsu.media.novel

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Bundle
import android.os.Environment
import android.os.Handler
import android.os.Looper
import android.os.Parcelable
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.ConcatAdapter
import androidx.recyclerview.widget.LinearLayoutManager
import ani.dantotsu.databinding.FragmentAnimeWatchBinding
import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.download.novel.NovelDownloaderService
import ani.dantotsu.download.novel.NovelServiceDataSingleton
import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaDetailsViewModel
import ani.dantotsu.media.novel.novelreader.NovelReaderActivity
import ani.dantotsu.navBarHeight
import ani.dantotsu.parsers.ShowResponse
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.File

class NovelReadFragment : Fragment(),
    DownloadTriggerCallback,
    DownloadedCheckCallback {

    private var _binding: FragmentAnimeWatchBinding? = null
    private val binding get() = _binding!!
    private val model: MediaDetailsViewModel by activityViewModels()

    private lateinit var media: Media
    var source = 0
    lateinit var novelName: String

    private lateinit var headerAdapter: NovelReadAdapter
    private lateinit var novelResponseAdapter: NovelResponseAdapter
    private var progress = View.VISIBLE

    private var continueEp: Boolean = false
    var loaded = false

    override fun downloadTrigger(novelDownloadPackage: NovelDownloadPackage) {
        Log.e("downloadTrigger", novelDownloadPackage.link)
        val downloadTask = NovelDownloaderService.DownloadTask(
            title = media.mainName(),
            chapter = novelDownloadPackage.novelName,
            downloadLink = novelDownloadPackage.link,
            originalLink = novelDownloadPackage.originalLink,
            sourceMedia = media,
            coverUrl = novelDownloadPackage.coverUrl,
            retries = 2,
        )
        NovelServiceDataSingleton.downloadQueue.offer(downloadTask)
        CoroutineScope(Dispatchers.IO).launch {

            if (!NovelServiceDataSingleton.isServiceRunning) {
                val intent = Intent(context, NovelDownloaderService::class.java)
                withContext(Dispatchers.Main) {
                    ContextCompat.startForegroundService(requireContext(), intent)
                }
                NovelServiceDataSingleton.isServiceRunning = true
            }

        }
    }

    override fun downloadedCheckWithStart(novel: ShowResponse): Boolean {
        val downloadsManager = Injekt.get<DownloadsManager>()
        if (downloadsManager.queryDownload(
                DownloadedType(
                    media.mainName(),
                    novel.name,
                    DownloadedType.Type.NOVEL
                )
            )
        ) {
            val file = File(
                context?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
                "${DownloadsManager.novelLocation}/${media.mainName()}/${novel.name}/0.epub"
            )
            if (!file.exists()) return false
            val fileUri = FileProvider.getUriForFile(
                requireContext(),
                "${requireContext().packageName}.provider",
                file
            )
            val intent = Intent(context, NovelReaderActivity::class.java).apply {
                action = Intent.ACTION_VIEW
                setDataAndType(fileUri, "application/epub+zip")
                flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
            }
            startActivity(intent)
            return true
        } else {
            return false
        }
    }

    override fun downloadedCheck(novel: ShowResponse): Boolean {
        val downloadsManager = Injekt.get<DownloadsManager>()
        return downloadsManager.queryDownload(
            DownloadedType(
                media.mainName(),
                novel.name,
                DownloadedType.Type.NOVEL
            )
        )
    }

    override fun deleteDownload(novel: ShowResponse) {
        val downloadsManager = Injekt.get<DownloadsManager>()
        downloadsManager.removeDownload(
            DownloadedType(
                media.mainName(),
                novel.name,
                DownloadedType.Type.NOVEL
            )
        )
    }

    private val downloadStatusReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            if (!this@NovelReadFragment::novelResponseAdapter.isInitialized) return
            when (intent.action) {
                ACTION_DOWNLOAD_STARTED -> {
                    val link = intent.getStringExtra(EXTRA_NOVEL_LINK)
                    link?.let {
                        novelResponseAdapter.startDownload(it)
                    }
                }

                ACTION_DOWNLOAD_FINISHED -> {
                    val link = intent.getStringExtra(EXTRA_NOVEL_LINK)
                    link?.let {
                        novelResponseAdapter.stopDownload(it)
                    }
                }

                ACTION_DOWNLOAD_FAILED -> {
                    val link = intent.getStringExtra(EXTRA_NOVEL_LINK)
                    link?.let {
                        novelResponseAdapter.purgeDownload(it)
                    }
                }

                ACTION_DOWNLOAD_PROGRESS -> {
                    val link = intent.getStringExtra(EXTRA_NOVEL_LINK)
                    val progress = intent.getIntExtra("progress", 0)
                    link?.let {
                        novelResponseAdapter.updateDownloadProgress(it, progress)
                    }
                }
            }
        }
    }

    var response: List<ShowResponse>? = null
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val intentFilter = IntentFilter().apply {
            addAction(ACTION_DOWNLOAD_STARTED)
            addAction(ACTION_DOWNLOAD_FINISHED)
            addAction(ACTION_DOWNLOAD_FAILED)
            addAction(ACTION_DOWNLOAD_PROGRESS)
        }

        ContextCompat.registerReceiver(
            requireContext(),
            downloadStatusReceiver,
            intentFilter,
            ContextCompat.RECEIVER_EXPORTED
        )

        binding.animeSourceRecycler.updatePadding(bottom = binding.animeSourceRecycler.paddingBottom + navBarHeight)

        binding.animeSourceRecycler.layoutManager = LinearLayoutManager(requireContext())
        model.scrolledToTop.observe(viewLifecycleOwner) {
            if (it) binding.animeSourceRecycler.scrollToPosition(0)
        }

        continueEp = model.continueMedia ?: false
        model.getMedia().observe(viewLifecycleOwner) {
            if (it != null) {
                media = it
                novelName = media.userPreferredName
                progress = View.GONE
                binding.mediaInfoProgressBar.visibility = progress
                if (!loaded) {
                    val sel = media.selected
                    searchQuery = sel?.server ?: media.name ?: media.nameRomaji
                    headerAdapter = NovelReadAdapter(media, this, model.novelSources)
                    novelResponseAdapter = NovelResponseAdapter(
                        this,
                        this,
                        this
                    )  // probably a better way to do this but it works
                    binding.animeSourceRecycler.adapter =
                        ConcatAdapter(headerAdapter, novelResponseAdapter)
                    loaded = true
                    Handler(Looper.getMainLooper()).postDelayed({
                        search(searchQuery, sel?.sourceIndex ?: 0, auto = sel?.server == null)
                    }, 100)
                }
            }
        }
        model.novelResponses.observe(viewLifecycleOwner) {
            if (it != null) {
                response = it
                searching = false
                novelResponseAdapter.submitList(it)
                headerAdapter.progress?.visibility = View.GONE
            }
        }
    }

    lateinit var searchQuery: String
    private var searching = false
    fun search(query: String, source: Int, save: Boolean = false, auto: Boolean = false) {
        if (!searching) {
            novelResponseAdapter.clear()
            searchQuery = query
            headerAdapter.progress?.visibility = View.VISIBLE
            lifecycleScope.launch(Dispatchers.IO) {
                if (auto || query == "") model.autoSearchNovels(media)
                else model.searchNovels(query, source)
            }
            searching = true
            if (save) {
                val selected = model.loadSelected(media)
                selected.server = query
                model.saveSelected(media.id, selected)
            }
        }
    }

    fun onSourceChange(i: Int) {
        val selected = model.loadSelected(media)
        selected.sourceIndex = i
        source = i
        selected.server = null
        model.saveSelected(media.id, selected)
        media.selected = selected
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = FragmentAnimeWatchBinding.inflate(inflater, container, false)
        return _binding?.root
    }

    override fun onDestroy() {
        model.mangaReadSources?.flushText()
        requireContext().unregisterReceiver(downloadStatusReceiver)
        super.onDestroy()
    }

    private var state: Parcelable? = null
    override fun onResume() {
        super.onResume()
        binding.mediaInfoProgressBar.visibility = progress
        binding.animeSourceRecycler.layoutManager?.onRestoreInstanceState(state)
    }

    override fun onPause() {
        super.onPause()
        state = binding.animeSourceRecycler.layoutManager?.onSaveInstanceState()
    }

    companion object {
        const val ACTION_DOWNLOAD_STARTED = "ani.dantotsu.ACTION_DOWNLOAD_STARTED"
        const val ACTION_DOWNLOAD_FINISHED = "ani.dantotsu.ACTION_DOWNLOAD_FINISHED"
        const val ACTION_DOWNLOAD_FAILED = "ani.dantotsu.ACTION_DOWNLOAD_FAILED"
        const val ACTION_DOWNLOAD_PROGRESS = "ani.dantotsu.ACTION_DOWNLOAD_PROGRESS"
        const val EXTRA_NOVEL_LINK = "extra_novel_link"
    }
}

interface DownloadTriggerCallback {
    fun downloadTrigger(novelDownloadPackage: NovelDownloadPackage)
}

interface DownloadedCheckCallback {
    fun downloadedCheck(novel: ShowResponse): Boolean
    fun downloadedCheckWithStart(novel: ShowResponse): Boolean
    fun deleteDownload(novel: ShowResponse)
}