package app.furmeet.events.system.schedule.fragments

import android.app.AlertDialog
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.content.res.ColorStateList
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageView
import android.widget.Space
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.core.graphics.toColor
import androidx.fragment.app.Fragment
import androidx.viewpager.widget.ViewPager
import app.furmeet.events.system.AssetImageViewerActivity
import app.furmeet.events.system.CountdownActivity
import app.furmeet.events.system.helper.*
import app.furmeet.events.system.helper.observables.Observable
import app.furmeet.events.system.helper.observables.Observer
import app.furmeet.events.system.notification.AppJobScheduler
import app.furmeet.events.system.notification.PastNotificationsActivity
import app.furmeet.events.system.schedule.EventActivity
import app.furmeet.events.system.schedule.abstractDataTypes.dataholder.PeekableEvent
import app.furmeet.events.system.schedule.abstractDataTypes.managed.Actions
import app.furmeet.events.system.schedule.abstractDataTypes.managed.AttendeeConFavorite
import app.furmeet.events.system.schedule.managers.ScheduleManagerGetter
import app.furmeet.events.bff.R
import app.furmeet.events.bff.databinding.*
import java.util.*
import kotlin.math.floor
import kotlin.math.roundToInt

class ConventionSummaryFragment : FragmentWithSafeActivity(), Observer<Any?> {
    companion object {
        fun newInstance(): Fragment {
            return ConventionSummaryFragment().apply {
                arguments = Bundle()
            }
        }
    }

    override fun update(observable: Observable<Any?>, args: Any?) {
        safeActivity.runOnUiThreadWithLogger {
            updateView()
        }
    }

    lateinit var binding: FragmentScheduleSummaryBinding
    val scheduleManager = ScheduleManagerGetter[safeActivity]
    lateinit var rootView: View
    lateinit var timer: Timer
    lateinit var onlineUpdateTimer: Timer

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
        rootView = inflater.inflate(R.layout.fragment_schedule_summary, container, false)
        binding = FragmentScheduleSummaryBinding.bind(rootView)
        rootView.visibility = View.INVISIBLE
        rootView.findViewById<View>(R.id.urgentMessageLayout).visibility = View.INVISIBLE
        rootView.findViewById<View>(R.id.communications).visibility = View.INVISIBLE
        binding.registrationTierSection.visibility = View.INVISIBLE
        rootView.findViewById<View>(R.id.countdown).visibility = View.INVISIBLE
        binding.whatsGoingOnSection.visibility = View.INVISIBLE
        binding.comingSoonSection.visibility = View.INVISIBLE
        binding.mapsSection.visibility = View.INVISIBLE
        binding.shareReminder.visibility = View.INVISIBLE
        binding.readTheRules.visibility = View.INVISIBLE
        updateView()
        timer = Timer()
        timer.scheduleAtFixedRate(TimerTask {
            safeActivity.runOnUiThreadWithLogger {
                rootView.let {
                    this@ConventionSummaryFragment.updateView()
                }
            }
        }, 3000, 30 * 1000)
        onlineUpdateTimer = Timer()
        onlineUpdateTimer.scheduleAtFixedRate(TimerTask {
            doAsyncWithLogger {
                AppJobScheduler.runNow(safeActivity)
            }
        }, 1000, 15 * 60 * 1000)
        scheduleManager.addObserver(this)
        return rootView
    }

    private fun updateView() {
        doAsync {
            safeActivity.runOnUiThread {
                rootView.visibility = View.VISIBLE
            }
            val conventionTime = scheduleManager.conventionTime
            val context = rootView.context
            val inflater = context.layoutInflater
            val eventCountdown = scheduleManager.nextEditionStartCountdown

            val hashtag = scheduleManager.convention.hashtagReminder
            val maps = scheduleManager.convention.maps

            // Urgent Message
            doAsync {
                val urgentMessage = scheduleManager.conventionSeries.urgentMessage?.card
                safeActivity.runOnUiThread {
                    if (urgentMessage == null)
                        rootView.findViewById<View>(R.id.urgentMessageLayout).visibility = View.GONE
                    else {
                        rootView.findViewById<View>(R.id.urgentMessageLayout).visibility = View.VISIBLE
                        rootView.findViewById<TextView>(R.id.urgentMessageName).text = urgentMessage.name.solved
                        rootView.findViewById<TextView>(R.id.urgentMessageDescription).text = urgentMessage.description.solved
                        rootView.findViewById<ImageView>(R.id.urgentMessageIcon).setImageResource(urgentMessage.icon.drawableId)
                        rootView.findViewById<View>(R.id.urgentMessageLayout).setOnClickListener {
                            safeActivity.findViewById<ViewPager>(R.id.container).currentItem = 1
                        }
                    }
                }
            }

            // Announcements
            doAsync {
                val latestAnnouncementMillis = scheduleManager.conventionSeries.announcements.map { it.created.timeInMillis }
                    .maxOrNull()
                        ?: 0L
                safeActivity.runOnUiThread {
                    if (scheduleManager.conventionSeries.announcements.isEmpty())
                        rootView.findViewById<View>(R.id.communications).visibility = View.GONE
                    else {
                        rootView.findViewById<View>(R.id.communications).visibility = View.VISIBLE
                        rootView.findViewById<TextView>(R.id.communicationsDescription).text =
                                R.string.past_notifications_last.getString().format(
                                        latestAnnouncementMillis.formatAsTextuallyPleasantDate()
                                )
                        rootView.findViewById<View>(R.id.communications).setOnClickListener {
                            safeActivity.findViewById<ViewPager>(R.id.container).currentItem =
                                    scheduleManager.convention.days.size +
                                            1 +
                                            if (scheduleManager.conventionSeries.urgentMessage == null) 0 else 1
                        }
                        rootView.findViewById<View>(R.id.communications).setOnLongClickListener {
                            safeActivity.startActivity(Intent(context, PastNotificationsActivity::class.java))
                            true
                        }
                    }
                }
            }
            // Registration tier
            doAsync {
                safeActivity.runOnUiThread {
                    if (scheduleManager.attendeeFavorites.registrationTier.benefits.isEmpty()) {
                        binding.textRegistrationTier.setTextColor(ContextCompat.getColor(context, R.color.colorBase24).asColorStateList)
                        binding.imageView.imageTintList = ContextCompat.getColor(context, R.color.colorBase25).asColorStateList
                        binding.dropDownView.imageTintList = ContextCompat.getColor(context, R.color.colorBase25).asColorStateList
                        binding.textRegistrationBenefits.setTextColor(ContextCompat.getColor(context, R.color.colorBase26).asColorStateList)
                        //rootView.registrationTierSection.backgroundTintList = ContextCompat.getColor(context, R.color.colorBase27).asColorStateList
                    } else {
                        binding.textRegistrationTier.setTextColor(ContextCompat.getColor(context, R.color.colorBase03).asColorStateList)
                        binding.imageView.imageTintList = ContextCompat.getColor(context, R.color.colorBase04).asColorStateList
                        binding.dropDownView.imageTintList = ContextCompat.getColor(context, R.color.colorBase04).asColorStateList
                        binding.textRegistrationBenefits.setTextColor(ContextCompat.getColor(context, R.color.colorBase05).asColorStateList)
                        //rootView.registrationTierSection.backgroundTintList = ContextCompat.getColor(context, R.color.colorBase21).asColorStateList
                    }
                    binding.textRegistrationTier.text = scheduleManager.attendeeFavorites.registrationTier.names.solved
                    binding.textRegistrationBenefits.text = scheduleManager.attendeeFavorites.registrationTier.benefits.size.run {
                        pluralize(
                                R.string.schedule_tierbenefit_none.getString(),
                                R.string.schedule_tierbenefit_singular.getString().format(this),
                                R.string.schedule_tierbenefit_plural.getString().format(this)
                        )
                    }
                    binding.registrationTierSection.setOnClickListener {
                        val dialog = AlertDialog.Builder(safeActivity).create()
                        dialog.setTitle(R.string.schedule_registrationtier_question.getString())
                        val scrollerBinding = DialogviewScheduleRegistrationTiersBinding.inflate(inflater)
                        val scroller = scrollerBinding.root
                        scheduleManager.convention.registrationTiers.sortedBy { it.level }.forEach {
                            val tierBinding = DialogviewScheduleRegistrationTierBinding.inflate(inflater);
                            scrollerBinding.container.addView((tierBinding.root).apply {
                                tierBinding.tier.text = it.names.solved
                                it.benefits.forEach {
                                    tierBinding.benefitsList.addView(TextView(context).apply {
                                        this.text = it.solved
                                        this.setTextColor(scroller.context.getColorCompat(R.color.colorBase05))
                                    })
                                }
                                if (it.benefits.isEmpty()) tierBinding.ticket.visibility = View.GONE
                                setOnClickListener { _ ->
                                    dialog.dismiss()
                                    scheduleManager.attendeeFavorites.registrationTier = it
                                }
                            })
                        }
                        dialog.setView(scroller)
                        dialog.setButton(AlertDialog.BUTTON_NEUTRAL, R.string.dialog_cancel.getString()) { dialogInterface: DialogInterface, _: Int -> dialogInterface.dismiss() }
                        val callbacks =
                                (scheduleManager.convention.registrationLinks.filter { scheduleManager.conventionTime in it.lifetime } + listOf(null, null))
                                        .subList(0, 2).let {
                                            Pair(it.getOrNull(0), it.getOrNull(1))
                                        }.let {
                                            listOf(
                                                    Pair(it.first, AlertDialog.BUTTON_POSITIVE),
                                                    Pair(it.second, AlertDialog.BUTTON_NEGATIVE)
                                            )
                                        }.map {
                                            it.first?.run {
                                                dialog.setButton(it.second, name.solved) { dialogInterface: DialogInterface, _: Int ->
                                                    val i = Intent(Intent.ACTION_VIEW)
                                                    i.data = Uri.parse(url.solved)
                                                    dialog.context.startActivity(i)
                                                    dialogInterface.dismiss()
                                                }
                                                fun() {
                                                    this.color?.let { color -> dialog.getButton(it.second).setTextColor(color) }
                                                }
                                            }
                                        }
                        dialog.setOnShowListener { callbacks.filterNotNull().forEach { it() } }
                        dialog.show()
                    }
                    if (!scheduleManager.attendeeFavorites.registrationTierSelectorShown) {
                        if (scheduleManager.convention.skipRegistrationTier.not()) {
                            binding.registrationTierSection.performClick()
                        } else {
                            scheduleManager.attendeeFavorites.registrationTier = scheduleManager.convention.registrationTiers.maxByOrNull { it.level }!!
                        }
                        scheduleManager.attendeeFavorites.registrationTierSelectorShown = true
                    }
                    binding.registrationTierSection.visibility = if (scheduleManager.convention.skipRegistrationTier) View.GONE else View.VISIBLE
                }
            }
            // Maps
            safeActivity.runOnUiThread {
                val mapButtons = binding.mapButtons
                mapButtons.removeAllViews()
                maps.withIndex().forEach {
                    val isLast = it.index + 1 >= maps.size
                    Button(rootView.context).apply {
                        text = it.value.name.solved
                        setOnClickListener { view ->
                            Intent(view.context, AssetImageViewerActivity::class.java).apply {
                                putExtra("asset", it.value.image.solved)
                                startActivity(this)
                            }
                        }
                        setTextColor(mapButtons.context.getColorCompat(R.color.colorBase04).asColorStateList)
                        backgroundTintList = mapButtons.context.getColorCompat(R.color.colorBase18).asColorStateList
                        mapButtons.addView(this)
                    }
                    if (!isLast) {
                        Space(rootView.context).apply {
                            minimumWidth = 4.asDpToPx.roundToInt()
                            minimumHeight = 0
                            mapButtons.addView(this)
                        }
                    }
                }
                binding.mapsSection.visibility = if (maps.isEmpty()) View.GONE else View.VISIBLE
            }
            // Countdown
            uiThread {
                if (eventCountdown == null)
                    rootView.findViewById<View>(R.id.countdown).visibility = View.GONE
                else {
                    rootView.findViewById<View>(R.id.countdown).visibility = View.VISIBLE
                    rootView.findViewById<TextView>(R.id.countdownTitle).text = context
                            .getString(R.string.thingsLacking_to)
                            .format(
                                    eventCountdown.let { (f, s, t) ->
                                        "%dd %dh %dm".format(f, s, t)
                                    }
                            )
                }
                rootView.findViewById<View>(R.id.countdown).setOnClickListener {
                    Intent(safeActivity, CountdownActivity::class.java).let {
                        it.putExtra("label", scheduleManager.convention.genericName)
                        it.putExtra("counter", scheduleManager.nextEditionStartTime)
                        startActivity(it)
                    }
                }
            }
            doAsync {
                val allHappening = scheduleManager.convention.events
                        .filterNot {
                            it.isCustom
                        }.map {
                            AttendeeConFavorite(scheduleManager.convention, it)
                        } +
                        scheduleManager.attendeeFavorites.toList().filter {
                            it.isCustom
                        }
                doAsync {
                    val happeningNow = allHappening
                            .filter { conventionTime in it.timeRange }
                            .sortedBy { -it.timeRange.start.timeInMillis }
                    uiThread {
                        binding.eventsNowDescription.text = happeningNow.size.run {
                            pluralize(
                                    R.string.schedule_now_no_event.getString(),
                                    R.string.schedule_now_n_event.getString().format(this),
                                    R.string.schedule_now_n_events.getString().format(this)
                            )
                        }
                        binding.eventsNowContainer.removeAllViews()
                        val accentColor = scheduleManager.convention.colorScheme?.accentColor?.asColorStateList
                        happeningNow.forEach { binding.eventsNowContainer.addView(renderEventCard(context, it, accentColor)) }
                        binding.whatsGoingOnSection.visibility = View.VISIBLE
                    }
                }
                doAsync {
                    val nextDayPart = allHappening
                            .sortedBy {
                                it.timeRange.start.timeInMillis
                            }
                            .firstOrNull {
                                it.timeRange.start.timeInMillis > conventionTime.timeInMillis
                            }
                            ?.timeRange
                            ?.start
                            ?.dayRange
                            ?.split(scheduleManager.convention.splitDayIn)
                            ?.dropWhile {
                                it.start.timeInMillis <= conventionTime.timeInMillis
                            }
                            ?.getOrNull(0)
                    val happeningNext = nextDayPart?.let { part ->
                        allHappening.filter {
                            it.timeRange.start in part
                        }
                    } ?: listOf()
                    uiThread {
                        binding.eventsComingDescription.text = happeningNext.size.run {
                            pluralize(
                                    R.string.schedule_coming_no_event.getString(),
                                    R.string.schedule_coming_n_event.getString().format(this),
                                    R.string.schedule_coming_n_events.getString().format(this)
                            )
                        }
                        binding.eventsComingContainer.removeAllViews()
                        val accentColor = scheduleManager.convention.colorScheme?.accentColor?.asColorStateList
                        happeningNext.forEach { binding.eventsComingContainer.addView(renderEventCard(context, it, accentColor)) }
                        binding.comingSoonSection.visibility = View.VISIBLE
                    }
                }
            }
            // 6-2-1 rule
            doAsync {
                safeActivity.runOnUiThread {
                    binding.r621watcher.removeAllViews()
                    if (scheduleManager.convention.r621checker) {
                        val favorites = scheduleManager.attendeeFavorites.toList()
                        val r621watchedDays =
                                scheduleManager.convention.days.map {
                                    it.dayRange
                                }.filter {
                                    it.start.timeInMillis > scheduleManager.convention.startCalendarOn.timeInMillis
                                }
                        r621watchedDays.forEach { dayRange ->
                            val todayFavorites = favorites.filter { it.timeRange.start in dayRange }
                            val sleepFavorites = todayFavorites.filter { it.kind == Actions.Intention.SLEEP }.filterNot { sleep ->
                                todayFavorites.filter { sleep.timeRange in it.timeRange }.size > 1
                            }
                            val eatFavorites = todayFavorites.filter { it.kind == Actions.Intention.EAT }
                            val showerFavorites = todayFavorites.filter { it.kind == Actions.Intention.SHOWER }
                            val daySegment = 24.0 / scheduleManager.convention.splitDayIn
                            if (
                                    ((sleepFavorites.size * daySegment) < 6)
                                    or
                                    ((eatFavorites.size * daySegment) < 2)
                                    or
                                    ((showerFavorites.size * daySegment) < 1)
                            ) {
                                binding.r621watcher.addView(
                                        rule621renderer(
                                                context,
                                                dayRange.start,
                                                sleepFavorites.size * daySegment,
                                                eatFavorites.size * daySegment,
                                                showerFavorites.size * daySegment
                                        )
                                )
                            }
                        }
                    }
                }
            }
            // Share reminder
            doAsync {
                safeActivity.runOnUiThread {
                    if (hashtag == null) {
                        binding.shareReminder.visibility = View.GONE
                    } else {
                        binding.shareReminderTitle.text = hashtag
                        binding.shareReminder.setOnClickListener { context.toClipboard(hashtag, "hashtag", true) }
                    }
                    binding.shareReminder.visibility = View.VISIBLE
                }
            }
            // Read the rules
            doAsync {
                safeActivity.runOnUiThread {
                    binding.readTheRules.setOnClickListener {
                        safeActivity.findViewById<ViewPager>(R.id.container).currentItem = scheduleManager.convention.days.size + 2 +
                                if (scheduleManager.conventionSeries.urgentMessage == null) 0 else 1
                    }
                    binding.readTheRules.visibility = View.VISIBLE
                }
            }
        }
    }

    private fun renderEventCard(context: Context, attendeeConFavorite: AttendeeConFavorite, starColor: ColorStateList? = null): View {
        val binding = FragmentScheduleSummaryEventitemBinding.inflate(context.layoutInflater)
        val view = binding.root
        val event = attendeeConFavorite.findInConvention(scheduleManager.convention)
        binding.startDay.text =
            attendeeConFavorite.timeRange.start.timeInMillis.formatAsDayOfWeek()
                .replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }
        binding.startText.text = attendeeConFavorite.timeRange.start.timeInMillis.formatAsHoursAndMinutes()
        binding.finishText.text = attendeeConFavorite.timeRange.finish.timeInMillis.formatAsHoursAndMinutes()
        binding.eventTitle.text = attendeeConFavorite.getString()
        binding.eventSubtitle.text = attendeeConFavorite.getSecondaryString()
        starColor?.let { binding.star.imageTintList = it }
        if (attendeeConFavorite in scheduleManager.attendeeFavorites) {
            binding.starButton.setOnClickListener {
                val dialog = AlertDialog.Builder(safeActivity).create()
                dialog.setTitle(context.getString(R.string.schedule_action_interest_remove))
                dialog.setButton(AlertDialog.BUTTON_NEUTRAL, context.getString(R.string.dialog_cancel)) { dialogInterface, _ ->
                    dialogInterface.dismiss()
                }
                dialog.setButton(AlertDialog.BUTTON_NEGATIVE, context.getString(R.string.list_action_remove)) { dialogInterface, _ ->
                    dialogInterface.dismiss()
                    scheduleManager.attendeeFavorites.remove(attendeeConFavorite)
                    updateView()
                }
                dialog.show()
            }
        } else {
            binding.star.setImageResource(R.drawable.ic_star_border_black_24dp)
            binding.starButton.setOnClickListener {
                binding.star.setImageResource(R.drawable.ic_star_half_black_24dp)
                scheduleManager.attendeeFavorites.add(attendeeConFavorite)
                safeActivity.begForNotificationPermission()
                updateView()
            }
        }
        if (event != null) {
            if (
                    event.hiddenFromTimeTable
                    or
                    (scheduleManager.attendeeFavorites.registrationTier.level !in event.attendableBy.map { it.level })
            ) {
                binding.star.visibility = View.INVISIBLE
                binding.starButton.setOnClickListener { }
            }
        }
        if (event?.language == null) {
            binding.flag.visibility = View.GONE
        } else {
            binding.flag.setImageResource(event.language.drawableId)
        }
        view.setOnClickListener {
            context.startActivity(
                    Intent(safeActivity, EventActivity::class.java).apply {
                        putExtra("favorite", attendeeConFavorite)
                        putExtra("offerStar", true)
                    }
            )
        }
        bindPeekAndPop(
                safeActivity,
                view as ViewGroup,
                view,
                if (binding.star.visibility == View.INVISIBLE)
                    null
                else
                    attendeeConFavorite in scheduleManager.attendeeFavorites,
                { doAsync { safeActivity.runOnUiThread { binding.starButton.callOnClick() } } },
                { doAsync { safeActivity.runOnUiThread { view.callOnClick() } } },
                scheduleManager.convention.events.firstOrNull {
                    it.conbookId == attendeeConFavorite.uuid
                }?.let { PeekableEvent(it) }
                        ?: PeekableEvent(attendeeConFavorite)
        )
        return view
    }

    private fun rule621renderer(context: Context, day: GregorianCalendar, sleep: Double, meals: Double, showers: Double): View {
        val binding = FragmentScheduleSummaryR621violationBinding.inflate(context.layoutInflater)
        val view = binding.root
        binding.r621Description.text = context.getString(R.string.on_something).format(day.timeInMillis.formatAsLongDayOfWeekWithNumber())
        binding.r621SleepMinimum.text = context.getString(R.string.r621_minimum_is_d).format(6)
        binding.r621EatMinimum.text = context.getString(R.string.r621_minimum_is_d).format(2)
        binding.r621ShowerMinimum.text = context.getString(R.string.r621_minimum_is_d).format(1)
        binding.r621SleepFound.text = context.getString(R.string.r621_found_d).format(floor(sleep).roundToInt())
        binding.r621EatFound.text = context.getString(R.string.r621_found_d).format(floor(meals).roundToInt())
        binding.r621ShowerFound.text = context.getString(R.string.r621_found_d).format(floor(showers).roundToInt())
        if (sleep < 6) binding.r621SleepFound.setTextColor(context.getColorCompat(R.color.colorBase06))
        if (meals < 2) binding.r621EatFound.setTextColor(context.getColorCompat(R.color.colorBase06))
        if (showers < 1) binding.r621ShowerFound.setTextColor(context.getColorCompat(R.color.colorBase06))
        return binding.root
    }
}