package com.remax.visualnovel.widget.imageviewer

import android.graphics.Color
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.Window
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import com.remax.visualnovel.R
import com.remax.visualnovel.databinding.FragmentImageViewerDialogBinding
import com.remax.visualnovel.widget.imageviewer.adapter.ImageViewerAdapter
import com.remax.visualnovel.widget.imageviewer.core.Components
import com.remax.visualnovel.widget.imageviewer.core.Components.requireDataProvider
import com.remax.visualnovel.widget.imageviewer.core.Components.requireOverlayCustomizer
import com.remax.visualnovel.widget.imageviewer.core.Components.requireTransformer
import com.remax.visualnovel.widget.imageviewer.core.Components.requireViewerCallback
import com.remax.visualnovel.widget.imageviewer.utils.Config
import com.remax.visualnovel.widget.imageviewer.utils.TransitionEndHelper
import com.remax.visualnovel.widget.imageviewer.utils.TransitionStartHelper
import com.remax.visualnovel.widget.imageviewer.utils.findViewWithKeyTag
import kotlin.math.max

open class ImageViewerDialogFragment : BaseDialogFragment() {
    private var innerBinding: FragmentImageViewerDialogBinding? = null
    private val binding get() = innerBinding!!
    private val viewModel by lazy { ViewModelProvider(this)[ImageViewerViewModel::class.java] }
    private val actions by lazy { ViewModelProvider(requireActivity())[ImageViewerActionViewModel::class.java] }
    private val userCallback by lazy { requireViewerCallback() }
    private val initKey by lazy { requireDataProvider().loadInitial().first().id() }
    private val transformer by lazy { requireTransformer() }
    private val adapter by lazy { ImageViewerAdapter(initKey) }
    private val taskId = 110
    private var submitPagingData = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        if (!Components.working) dismissAllowingStateLoss()
    }

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

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        adapter.setListener(adapterListener)
        (binding.viewer.getChildAt(0) as? RecyclerView?)?.let {
            it.clipChildren = false
            it.itemAnimator = null
        }
        binding.viewer.registerOnPageChangeCallback(pagerCallback)
        binding.viewer.orientation = Config.VIEWER_ORIENTATION
        binding.viewer.adapter = adapter

        requireOverlayCustomizer().provideView(binding.overlayView)?.let(binding.overlayView::addView)

        viewModel.pagingData.observe(viewLifecycleOwner) {
            submitPagingData = true
            adapter.submitData(viewLifecycleOwner.lifecycle, it)
        }
        viewModel.viewerUserInputEnabled.observe(viewLifecycleOwner) {
            binding.viewer.isUserInputEnabled = it ?: true
        }
        actions.actionEvent.observe(viewLifecycleOwner, Observer(::handle))
    }

    private fun handle(action: Pair<String, Any?>?) {
        when (action?.first) {
            ViewerActions.SET_CURRENT_ITEM -> binding.viewer.currentItem = max(action.second as Int, 0)
            ViewerActions.DISMISS -> onBackPressed()
            ViewerActions.REMOVE_ITEMS -> viewModel.remove(adapter, action.second) { onBackPressed() }
        }
    }

    private val adapterListener by lazy {
        object : ImageViewerAdapterListener {
            override fun onInit(viewHolder: RecyclerView.ViewHolder, position: Int) {
                TransitionStartHelper.start(this@ImageViewerDialogFragment, transformer.getView(initKey), viewHolder)
                binding.background.changeToBackgroundColor(Config.VIEWER_BACKGROUND_COLOR)
                userCallback.onInit(viewHolder, position)
            }

            override fun onDrag(viewHolder: RecyclerView.ViewHolder, view: View, fraction: Float) {
                binding.background.updateBackgroundColor(fraction, Config.VIEWER_BACKGROUND_COLOR, Color.TRANSPARENT)
                userCallback.onDrag(viewHolder, view, fraction)
            }

            override fun onRestore(viewHolder: RecyclerView.ViewHolder, view: View, fraction: Float) {
                binding.background.changeToBackgroundColor(Config.VIEWER_BACKGROUND_COLOR)
                userCallback.onRestore(viewHolder, view, fraction)
            }

            override fun onRelease(viewHolder: RecyclerView.ViewHolder, view: View) {
                val startView = (view.getTag(R.id.viewer_adapter_item_key) as? Long?)?.let { transformer.getView(it) }
                TransitionEndHelper.end(this@ImageViewerDialogFragment, startView, viewHolder)
                binding.background.changeToBackgroundColor(Color.TRANSPARENT)
                userCallback.onRelease(viewHolder, view)
            }
        }
    }

    private val pagerCallback by lazy {
        object : ViewPager2.OnPageChangeCallback() {
            override fun onPageScrollStateChanged(state: Int) {
                userCallback.onPageScrollStateChanged(state)
            }

            override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
                userCallback.onPageScrolled(position, positionOffset, positionOffsetPixels)
            }

            override fun onPageSelected(position: Int) {
                val currentKey = viewModel.snapshot[position].id()
                val holder = binding.viewer.findViewWithKeyTag(R.id.viewer_adapter_item_key, currentKey)
                    ?.getTag(R.id.viewer_adapter_item_holder) as? RecyclerView.ViewHolder?
                    ?: return

                if (submitPagingData) {
                    submitPagingData = false
                    viewerHandler.removeMessages(taskId)
                    viewerHandler.sendMessageDelayed(
                        Message.obtain(viewerHandler, taskId, position, 0, holder),
                        Config.VIEWER_FIRST_PAGE_SELECTED_DELAY
                    )
                    return
                }
                viewerHandler.removeMessages(taskId)
                Handler(Looper.getMainLooper()).post {
                    userCallback.onPageSelected(position, adapter.getPositionData(position), adapter.itemCount, holder)
                }
            }
        }
    }

    override fun showFailure(message: String?) {
        super.showFailure(message)
        Components.release()
    }

    override fun onDestroyView() {
        super.onDestroyView()
        viewerHandler.removeMessages(taskId)
        adapter.setListener(null)
        binding.viewer.unregisterOnPageChangeCallback(pagerCallback)
        binding.viewer.adapter = null
        innerBinding = null
        Components.release()
    }

    override fun onBackPressed() {
        if (TransitionStartHelper.transitionAnimating || TransitionEndHelper.transitionAnimating) return

        val currentKey = viewModel.snapshot[binding.viewer.currentItem].id()
        binding.viewer.findViewWithKeyTag(R.id.viewer_adapter_item_key, currentKey)?.let { endView ->
            val startView = transformer.getView(endView.getTag(R.id.viewer_adapter_item_key) as Long)
            binding.background.changeToBackgroundColor(Color.TRANSPARENT)

            (endView.getTag(R.id.viewer_adapter_item_holder) as? RecyclerView.ViewHolder?)?.let {
                TransitionEndHelper.end(this, startView, it)
                userCallback.onRelease(it, endView)
            }
        }
    }

    private val viewerHandler by lazy {
        Handler(Looper.getMainLooper()) {
            it.target.removeMessages(it.what)
            userCallback.onPageSelected(
                it.arg1,
                adapter.getPositionData(it.arg1),
                adapter.itemCount,
                it.obj as RecyclerView.ViewHolder
            )
            true
        }
    }

    open class Factory {
        open fun build(): ImageViewerDialogFragment = ImageViewerDialogFragment()
    }
}
