package app.furmeet.events.system.schedule.adapters

import android.app.Activity
import android.app.AlertDialog
import android.content.Intent
import android.content.res.ColorStateList
import android.view.View
import android.view.ViewGroup
import android.widget.ListView
import androidx.recyclerview.widget.DiffUtil
import app.furmeet.events.bff.R
import app.furmeet.events.system.helper.*
import app.furmeet.events.system.schedule.EventActivity
import app.furmeet.events.system.schedule.abstractDataTypes.GregorianCalendarRange
import app.furmeet.events.system.schedule.abstractDataTypes.StickyHourHeader
import app.furmeet.events.system.schedule.abstractDataTypes.convention.Event
import app.furmeet.events.system.schedule.abstractDataTypes.dataholder.PeekableEvent
import app.furmeet.events.system.schedule.abstractDataTypes.managed.AttendeeConFavorite
import app.furmeet.events.system.schedule.managers.ScheduleManager
import app.furmeet.events.system.schedule.viewHolders.RecyclerStickyViewHolder
import app.furmeet.events.system.schedule.viewHolders.RecyclerViewHolder
import app.furmeet.events.bff.databinding.ListitemScheduleEventlistSubitemHeaderBinding
import app.furmeet.events.bff.databinding.ListitemScheduleEventlistSubitemItemBinding
import com.brandongogetap.stickyheaders.exposed.StickyHeaderHandler

class DayHoursAdapter(
        private val activity: Activity,
        private val dayParts: List<GregorianCalendarRange>,
        private val scheduleManager: ScheduleManager
) : RecyclerViewAdapter(), StickyHeaderHandler {
    lateinit var stickyHourHeader: StickyHourHeader
    private val registrationTierLevel get() = scheduleManager.attendeeFavorites.registrationTier.level
    private fun getCurrentData() = dayParts.flatMap {
        listOf(StickyHourHeader(it)).map { item -> Triple(item, null, null) } +
                scheduleManager.convention.events.filter { ev ->
                    ev.timeRange.start in it
                }.sortedBy { ev ->
                    ev.titles.solved
                }.sortedBy { ev ->
                    ev.timeRange.start
                }.map { item ->
                    Triple(null, item, null)
                } +
                scheduleManager.attendeeFavorites.toList().filter { fav ->
                    (fav.timeRange.start in it) && fav.isCustom
                }.sortedBy { fav ->
                    fav.getString()
                }.sortedBy { fav ->
                    fav.timeRange.start
                }.map { item ->
                    Triple(null, null, item)
                }
    }.map {
        when {
            it.first != null -> {
                stickyHourHeader = it.first!!
                Triple(stickyHourHeader, null, Pair(null, false))
            }
            it.second != null -> {
                val event = it.second!!
                Triple(
                        stickyHourHeader,
                        PeekableEvent(event),
                        Pair(
                                event,
                                when {
                                    scheduleManager.attendeeFavorites.contains(event) -> true
                                    event.attendableBy.map { tier -> tier.level }.contains(registrationTierLevel) -> false
                                    else -> null
                                }))
            }
            it.third != null -> {
                val fav = it.third!!
                Triple(stickyHourHeader, PeekableEvent(fav), Pair(fav, true))
            }
            else -> throw IndexOutOfBoundsException("Malformed data")
        }
    }

    private var data = getCurrentData()

    override fun getAdapterData() = data.map { it.second ?: it.first }
    override fun getItemCount(): Int = data.size
    override fun getItemViewType(position: Int): Int = if (data[position].second == null) 0 else 1

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerViewHolder {
        return when (viewType) {
            0 -> RecyclerStickyViewHolder(parent.context.layoutInflater.inflate(R.layout.listitem_schedule_eventlist_subitem_header, parent, false))
            1 -> RecyclerViewHolder(parent.context.layoutInflater.inflate(R.layout.listitem_schedule_eventlist_subitem_item, parent, false))
            else -> throw IndexOutOfBoundsException("Unrecognizable ViewHolder type")
        }
    }

    override fun onBindViewHolder(holder: RecyclerViewHolder, position: Int) {
        when (getItemViewType(position)) {
            0 -> onBindHeaderViewHolder(holder, position)
            1 -> onBindItemViewHolder(holder, position)
            else -> throw IndexOutOfBoundsException("Unrecognizable ViewHolder type")
        }
    }

    private fun onBindItemViewHolder(holder: RecyclerViewHolder, position: Int) {
        val item = data[position]
        val viewGroup = holder.itemView as ViewGroup
        val binding = ListitemScheduleEventlistSubitemItemBinding.bind(viewGroup)
        val context = viewGroup.context
        val runtimeLanguage = context.runtimeLanguage
        val event = when (item.third.first) {
            is Event -> item.third.first as Event
            else -> null
        }
        val favorite = when (item.third.first) {
            is AttendeeConFavorite -> item.third.first as AttendeeConFavorite
            else -> AttendeeConFavorite(scheduleManager.convention, event!!)
        }
        val peekable = item.second!!
        val attending = item.third.second == true
        val attendable = item.third.second != null
        val needsDeletingConfirmation = event == null
        val firstLine = peekable.title[runtimeLanguage]
        val secondLine = null
                ?: peekable.subtitles?.get(runtimeLanguage).nullIfBlank()
                ?: peekable.places?.map { place -> place.names.solved!! }?.textuallyJoined.nullIfBlank()
                ?: ""
        val language = event?.language.nullIf { it == scheduleManager.conventionSeries.language }
        val timeRange = peekable.timeRange
        // Texts
        binding.startText.text = timeRange.start.timeInMillis.formatAsHoursAndMinutes()
        binding.finishText.text = timeRange.finish.timeInMillis.formatAsHoursAndMinutes()
        binding.eventTitle.text = firstLine
        binding.eventSubtitle.text = secondLine
        // Flag
        if (language == null) {
            binding.flag.visibility = View.GONE
        } else {
            binding.flag.visibility = View.VISIBLE
            binding.flag.setImageResource(language.drawableId)
        }
        // Star
        val starSetter = { state: Boolean ->
            binding.star.setImageResource(if (state) R.drawable.ic_star_black_24dp else R.drawable.ic_star_border_black_24dp)
        }
        starSetter(attending)
        binding.star.visibility = if (attendable) View.VISIBLE else View.INVISIBLE
        scheduleManager.convention.colorScheme?.accentColor?.let {
            binding.star.imageTintList = ColorStateList.valueOf(it)
        }
        // Fav click
        if (attendable) binding.starButton.setOnClickListener {
            starSetter(!attending)
            if (!attending){
                scheduleManager.attendeeFavorites.add(favorite)
                activity.begForNotificationPermission()
            }
            else {
                if (!needsDeletingConfirmation)
                    scheduleManager.attendeeFavorites.remove(favorite)
                else {
                    AlertDialog.Builder(activity)
                            .setTitle(activity.getString(R.string.schedule_action_interest_remove))
                            .setNeutralButton(activity.getString(R.string.dialog_cancel)) { dialogInterface, _ ->
                                dialogInterface.dismiss()
                            }
                            .setNegativeButton(activity.getString(R.string.list_action_remove)) { dialogInterface, _ ->
                                scheduleManager.attendeeFavorites.remove(favorite)
                                dialogInterface.dismiss()
                            }.create().show()
                }
            }
        }
        // Full click
        viewGroup.setOnClickListener {
            activity.startActivity(
                    Intent(activity, EventActivity::class.java).apply {
                        putExtra("favorite", favorite)
                        putExtra("offerStar", true)
                    }
            )
        }
        // Peek
        bindPeekAndPop(
                activity,
                viewGroup,
                viewGroup,
                if (!attendable) null else attending,
                { binding.starButton.callOnClick() },
                { viewGroup.callOnClick() },
                peekable
        )
    }

    private fun onBindHeaderViewHolder(holder: RecyclerViewHolder, position: Int) {
        val dayPart = data[position].first.date
        val start = dayPart.start.timeInMillis.formatAsHoursAndMinutes()
        val finish = dayPart.finish.timeInMillis.formatAsHoursAndMinutes()
        val binding = ListitemScheduleEventlistSubitemHeaderBinding.bind(holder.itemView)
        binding.time.text = start
        binding.addButton.setOnClickListener {
            val dialog = AlertDialog.Builder(activity).create()
            dialog.setTitle(R.string.schedule_add_what.getString().format(start, finish))
            val listView = ListView(activity)
            listView.adapter = HourlyPlannedEventDialogListAdapter(activity, dialog, dayPart)
            dialog.setView(listView)
            dialog.setButton(AlertDialog.BUTTON_NEUTRAL, R.string.dialog_cancel.getString()) { _, _ -> dialog.dismiss() }
            dialog.show()
        }
    }

    fun onDataChanged() {
        val oldRawData = data
        val newRawData = getCurrentData()
        val oldData = oldRawData.map { Triple(it.first, it.second, it.third.second) }
        val newData = newRawData.map { Triple(it.first, it.second, it.third.second) }
        val diff = DiffUtil.calculateDiff(ListDiffCallback(oldData, newData))
        activity.runOnUiThread {
            data = newRawData
            diff.dispatchUpdatesTo(this)
        }
    }
}