package app.furmeet.events.brasilfurfest.schedule.fragments

import android.app.AlertDialog
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
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.Space
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import app.furmeet.events.brasilfurfest.AssetImageViewerActivity
import app.furmeet.events.brasilfurfest.CountdownActivity
import com.adlerosn.brasilfurfest.R
import app.furmeet.events.brasilfurfest.helper.*
import app.furmeet.events.brasilfurfest.helper.observables.Observable
import app.furmeet.events.brasilfurfest.helper.observables.Observer
import app.furmeet.events.brasilfurfest.notification.AppJobScheduler
import app.furmeet.events.brasilfurfest.notification.PastNotificationsActivity
import app.furmeet.events.brasilfurfest.schedule.EventActivity
import app.furmeet.events.brasilfurfest.schedule.abstractDataTypes.dataholder.PeekableEvent
import app.furmeet.events.brasilfurfest.schedule.abstractDataTypes.managed.Actions
import app.furmeet.events.brasilfurfest.schedule.abstractDataTypes.managed.AttendeeConFavorite
import app.furmeet.events.brasilfurfest.schedule.managers.ScheduleManagerGetter
import kotlinx.android.synthetic.main.activity_schedule.*
import kotlinx.android.synthetic.main.dialogview_schedule_registration_tier.view.*
import kotlinx.android.synthetic.main.dialogview_schedule_registration_tiers.view.*
import kotlinx.android.synthetic.main.fragment_schedule_summary.view.*
import kotlinx.android.synthetic.main.fragment_schedule_summary_announcements.view.*
import kotlinx.android.synthetic.main.fragment_schedule_summary_countdown.view.*
import kotlinx.android.synthetic.main.fragment_schedule_summary_eventitem.view.*
import kotlinx.android.synthetic.main.fragment_schedule_summary_r621violation.view.*
import kotlinx.android.synthetic.main.fragment_schedule_summary_urgentmessage.view.*
import org.jetbrains.anko.doAsync
import org.jetbrains.anko.uiThread
import java.util.*
import kotlin.math.floor
import kotlin.math.roundToInt

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

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

    val safeActivity = ActivitiesForFragments["ScheduleActivity"]!!
    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)
        rootView.visibility = View.INVISIBLE
        rootView.urgentMessageLayout.visibility = View.INVISIBLE
        rootView.communications.visibility = View.INVISIBLE
        rootView.registrationTierSection.visibility = View.INVISIBLE
        rootView.countdown.visibility = View.INVISIBLE
        rootView.whatsGoingOnSection.visibility = View.INVISIBLE
        rootView.comingSoonSection.visibility = View.INVISIBLE
        rootView.mapsSection.visibility = View.INVISIBLE
        rootView.shareReminder.visibility = View.INVISIBLE
        rootView.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.urgentMessageLayout.visibility = View.GONE
                    else {
                        rootView.urgentMessageLayout.visibility = View.VISIBLE
                        rootView.urgentMessageName.text = urgentMessage.name.solved
                        rootView.urgentMessageDescription.text = urgentMessage.description.solved
                        rootView.urgentMessageIcon.setImageResource(urgentMessage.icon.drawableId)
                        rootView.urgentMessageLayout.setOnClickListener {
                            safeActivity.container.currentItem = 1
                        }
                    }
                }
            }

            // Announcements
            doAsync {
                val latestAnnouncementMillis = scheduleManager.conventionSeries.announcements.map { it.created.timeInMillis }.max()
                        ?: 0L
                safeActivity.runOnUiThread {
                    if (scheduleManager.conventionSeries.announcements.isEmpty())
                        rootView.communications.visibility = View.GONE
                    else {
                        rootView.communications.visibility = View.VISIBLE
                        rootView.communicationsDescription.text =
                                R.string.past_notifications_last.getString().format(
                                        latestAnnouncementMillis.formatAsTextuallyPleasantDate()
                                )
                        rootView.communications.setOnClickListener {
                            safeActivity.container.currentItem =
                                    scheduleManager.convention.days.size +
                                            1 +
                                            if (scheduleManager.conventionSeries.urgentMessage == null) 0 else 1
                        }
                        rootView.communications.setOnLongClickListener {
                            safeActivity.startActivity(Intent(context, PastNotificationsActivity::class.java))
                            true
                        }
                    }
                }
            }
            // Registration tier
            doAsync {
                safeActivity.runOnUiThread {
                    if (scheduleManager.attendeeFavorites.registrationTier.benefits.isEmpty()) {
                        rootView.textRegistrationTier.setTextColor(ContextCompat.getColor(context, R.color.colorBase24).asColorStateList)
                        rootView.imageView.imageTintList = ContextCompat.getColor(context, R.color.colorBase25).asColorStateList
                        rootView.dropDownView.imageTintList = ContextCompat.getColor(context, R.color.colorBase25).asColorStateList
                        rootView.textRegistrationBenefits.setTextColor(ContextCompat.getColor(context, R.color.colorBase26).asColorStateList)
                        //rootView.registrationTierSection.backgroundTintList = ContextCompat.getColor(context, R.color.colorBase27).asColorStateList
                    } else {
                        rootView.textRegistrationTier.setTextColor(ContextCompat.getColor(context, R.color.colorBase03).asColorStateList)
                        rootView.imageView.imageTintList = ContextCompat.getColor(context, R.color.colorBase04).asColorStateList
                        rootView.dropDownView.imageTintList = ContextCompat.getColor(context, R.color.colorBase04).asColorStateList
                        rootView.textRegistrationBenefits.setTextColor(ContextCompat.getColor(context, R.color.colorBase05).asColorStateList)
                        //rootView.registrationTierSection.backgroundTintList = ContextCompat.getColor(context, R.color.colorBase21).asColorStateList
                    }
                    rootView.textRegistrationTier.text = scheduleManager.attendeeFavorites.registrationTier.names.solved
                    rootView.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)
                        )
                    }
                    rootView.registrationTierSection.setOnClickListener {
                        val dialog = AlertDialog.Builder(safeActivity).create()
                        dialog.setTitle(R.string.schedule_registrationtier_question.getString())
                        val scroller = inflater.inflate(R.layout.dialogview_schedule_registration_tiers)
                        scheduleManager.convention.registrationTiers.sortedBy { it.level }.forEach {
                            scroller.container.addView(inflater.inflate(R.layout.dialogview_schedule_registration_tier).apply {
                                tier.text = it.names.solved
                                it.benefits.forEach {
                                    benefitsList.addView(TextView(context).apply {
                                        this.text = it.solved
                                        this.setTextColor(scroller.context.getColorCompat(R.color.colorBase05))
                                    })
                                }
                                if (it.benefits.isEmpty()) 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()) {
                            rootView.registrationTierSection.performClick()
                        } else {
                            scheduleManager.attendeeFavorites.registrationTier = scheduleManager.convention.registrationTiers.maxBy { it.level }!!
                        }
                        scheduleManager.attendeeFavorites.registrationTierSelectorShown = true
                    }
                    rootView.registrationTierSection.visibility = if (scheduleManager.convention.skipRegistrationTier) View.GONE else View.VISIBLE
                }
            }
            // Maps
            safeActivity.runOnUiThread {
                val mapButtons = rootView.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)
                        }
                    }
                }
                rootView.mapsSection.visibility = View.VISIBLE
            }
            // Countdown
            uiThread {
                if (eventCountdown == null)
                    rootView.countdown.visibility = View.GONE
                else {
                    rootView.countdown.visibility = View.VISIBLE
                    rootView.countdownTitle.text = context
                            .getString(R.string.thingsLacking_to)
                            .format(
                                    eventCountdown.let { (f, s, t) ->
                                        "%dd %dh %dm".format(f, s, t)
                                    }
                            )
                }
                rootView.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 {
                        rootView.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)
                            )
                        }
                        rootView.eventsNowContainer.removeAllViews()
                        happeningNow.forEach { rootView.eventsNowContainer.addView(renderEventCard(context, it)) }
                        rootView.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 {
                        rootView.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)
                            )
                        }
                        rootView.eventsComingContainer.removeAllViews()
                        happeningNext.forEach { rootView.eventsComingContainer.addView(renderEventCard(context, it)) }
                        rootView.comingSoonSection.visibility = View.VISIBLE
                    }
                }
            }
            // 6-2-1 rule
            doAsync {
                safeActivity.runOnUiThread {
                    rootView.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)
                            ) {
                                rootView.r621watcher.addView(
                                        rule621renderer(
                                                context,
                                                dayRange.start,
                                                sleepFavorites.size * daySegment,
                                                eatFavorites.size * daySegment,
                                                showerFavorites.size * daySegment
                                        )
                                )
                            }
                        }
                    }
                }
            }
            // Share reminder
            doAsync {
                safeActivity.runOnUiThread {
                    if (hashtag == null) {
                        rootView.shareReminder.visibility = View.GONE
                    } else {
                        rootView.shareReminderTitle.text = hashtag
                        rootView.shareReminder.setOnClickListener { context.toClipboard(hashtag, "hashtag", true) }
                    }
                    rootView.shareReminder.visibility = View.VISIBLE
                }
            }
            // Read the rules
            doAsync {
                safeActivity.runOnUiThread {
                    rootView.readTheRules.setOnClickListener {
                        safeActivity.container.currentItem = scheduleManager.convention.days.size + 2 +
                                if (scheduleManager.conventionSeries.urgentMessage == null) 0 else 1
                    }
                    rootView.readTheRules.visibility = View.VISIBLE
                }
            }
        }
    }

    private fun renderEventCard(context: Context, attendeeConFavorite: AttendeeConFavorite): View {
        val view = context.layoutInflater.inflate(R.layout.fragment_schedule_summary_eventitem)
        val event = attendeeConFavorite.findInConvention(scheduleManager.convention)
        view.startDay.text = attendeeConFavorite.timeRange.start.timeInMillis.formatAsDayOfWeek().capitalize()
        view.startText.text = attendeeConFavorite.timeRange.start.timeInMillis.formatAsHoursAndMinutes()
        view.finishText.text = attendeeConFavorite.timeRange.finish.timeInMillis.formatAsHoursAndMinutes()
        view.eventTitle.text = attendeeConFavorite.getString()
        view.eventSubtitle.text = attendeeConFavorite.getSecondaryString()
        if (attendeeConFavorite in scheduleManager.attendeeFavorites) {
            view.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 {
            view.star.setImageResource(R.drawable.ic_star_border_black_24dp)
            view.starButton.setOnClickListener {
                view.star.setImageResource(R.drawable.ic_star_half_black_24dp)
                scheduleManager.attendeeFavorites.add(attendeeConFavorite)
                updateView()
            }
        }
        if (event != null) {
            if (
                    event.hiddenFromTimeTable
                    or
                    (scheduleManager.attendeeFavorites.registrationTier.level !in event.attendableBy.map { it.level })
            ) {
                view.star.visibility = View.INVISIBLE
                view.starButton.setOnClickListener { }
            }
        }
        if (event?.language == null) {
            view.flag.visibility = View.GONE
        } else {
            view.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 (view.star.visibility == View.INVISIBLE)
                    null
                else
                    attendeeConFavorite in scheduleManager.attendeeFavorites,
                { doAsync { safeActivity.runOnUiThread { view.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 view = context.layoutInflater.inflate(R.layout.fragment_schedule_summary_r621violation)
        view.r621Description.text = context.getString(R.string.on_something).format(day.timeInMillis.formatAsLongDayOfWeekWithNumber())
        view.r621_sleep_minimum.text = context.getString(R.string.r621_minimum_is_d).format(6)
        view.r621_eat_minimum.text = context.getString(R.string.r621_minimum_is_d).format(2)
        view.r621_shower_minimum.text = context.getString(R.string.r621_minimum_is_d).format(1)
        view.r621_sleep_found.text = context.getString(R.string.r621_found_d).format(floor(sleep).roundToInt())
        view.r621_eat_found.text = context.getString(R.string.r621_found_d).format(floor(meals).roundToInt())
        view.r621_shower_found.text = context.getString(R.string.r621_found_d).format(floor(showers).roundToInt())
        if (sleep < 6) view.r621_sleep_found.setTextColor(context.getColorCompat(R.color.colorBase06))
        if (meals < 2) view.r621_eat_found.setTextColor(context.getColorCompat(R.color.colorBase06))
        if (showers < 1) view.r621_shower_found.setTextColor(context.getColorCompat(R.color.colorBase06))
        return view
    }
}