package de.sebse.fuplanner2.ui.schedule

import android.content.Context
import android.os.Bundle
import android.text.SpannableStringBuilder
import android.view.*
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import com.alamkanak.weekview.WeekView
import com.alamkanak.weekview.WeekViewEntity
import de.sebse.fuplanner2.R
import de.sebse.fuplanner2.database.AppDatabase
import de.sebse.fuplanner2.database.Course
import de.sebse.fuplanner2.database.Event
import de.sebse.fuplanner2.ui.schedule.MyCustomPagingAdapter.LoadMoreHandler
import de.sebse.fuplanner2.utils.color.getColor
import de.sebse.fuplanner2.utils.getHtmlSpannedString
import de.sebse.fuplanner2.utils.toTimeString
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.*

class ScheduleFragment : Fragment() {

    private val scheduleViewModel: ScheduleViewModel by viewModels { ScheduleViewModelFactory() }
    private var currentDate = Calendar.getInstance()
    private var weekView: WeekView? = null

    init {
        setHasOptionsMenu(true)
    }

    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
        inflater.inflate(R.menu.schedule_menu, menu)
        super.onCreateOptionsMenu(menu, inflater)
    }

    override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View? {
        val navController = findNavController()
        var alertCourse: Course? = null
        val alertDialog = AlertDialog.Builder(requireContext())
            .setCancelable(true)
            .setNegativeButton(R.string.close) { dialog, _ ->
                dialog.cancel()
            }
            .setPositiveButton(R.string.view_course) { _, _ ->
                alertCourse?.let { course ->
                    navController.navigate(ScheduleFragmentDirections.actionNavScheduleToCourseDetails(course.uid!!, course.title))
                }
            }
            .create()


        return inflater.inflate(R.layout.fragment_schedule, container, false).apply {
            weekView = findViewById<WeekView>(R.id.weekView).apply {
                val pagingAdapter = MyCustomPagingAdapter(object : LoadMoreHandler {
                    override fun setOnLoadMoreListener(startDate: Calendar, endDate: Calendar) =
                        scheduleViewModel.loadBetween(startDate.timeInMillis, endDate.timeInMillis)

                    override fun setOnRangeChangeListener(
                        firstVisibleDate: Calendar,
                        lastVisibleDate: Calendar
                    ) {
                        currentDate = firstVisibleDate.clone() as Calendar
                    }

                    override fun setOnEventClickListener(data: ContextEvent) {
                        alertDialog.run {
                            GlobalScope.launch {
                                val course: Course = withContext(Dispatchers.IO) {
                                    AppDatabase.getInstance().courseDao().getCourseById2(data.event.courseId)
                                }
                                @Suppress("BlockingMethodInNonBlockingContext")
                                val cs: CharSequence = SpannableStringBuilder().apply {
                                    this.append(context.getHtmlSpannedString(R.string.dialog_course_br, course.title))
                                    if (!data.event.location.isNullOrBlank()) {
                                        this.append(context.getHtmlSpannedString(R.string.dialog_location_br, data.event.location))
                                    }
                                    this.append(context.getHtmlSpannedString(
                                        R.string.dialog_time,
                                        data.event.startDateTime.toTimeString(context),
                                        (data.event.startDateTime + data.event.duration).toTimeString(context)
                                    ))
                                }
                                withContext(Dispatchers.Main) {
                                    alertCourse = course
                                    setTitle(data.event.title)
                                    setMessage(cs)
                                    show()
                                }
                            }
                        }
                    }
                })

                scheduleViewModel.eventModel.observe(viewLifecycleOwner, {
                    pagingAdapter.updateEntries(it.map { event -> ContextEvent(requireContext(), event) })
                })

                val todayDate = Calendar.getInstance()
                todayDate.firstDayOfWeek = Calendar.SATURDAY
                todayDate.set(Calendar.DAY_OF_WEEK, Calendar.SATURDAY)
                todayDate.add(Calendar.DAY_OF_WEEK, 2)
                scrollToDate(todayDate)
            }
        }
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        val id = item.itemId
        return when (id) {
            R.id.prev_week -> {
                currentDate.firstDayOfWeek = Calendar.TUESDAY
                currentDate.set(Calendar.DAY_OF_WEEK, Calendar.TUESDAY)
                currentDate.add(Calendar.DAY_OF_WEEK, -1)
                weekView?.scrollToDate(currentDate)
                true
            }
            R.id.next_week -> {
                currentDate.firstDayOfWeek = Calendar.MONDAY
                currentDate.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY)
                currentDate.add(Calendar.DAY_OF_WEEK, 7)
                weekView?.scrollToDate(currentDate)
                true
            }
            R.id.go_to_today -> {
                weekView?.scrollToDate(Calendar.getInstance())
                true
            }
            else -> super.onOptionsItemSelected(item)
        }
    }
}

data class ContextEvent(val actCtx: Context, val event: Event) {
    fun toWeekViewEvent(): WeekViewEntity {
        // Build the styling of the event, for instance background color and strike-through
        val style = WeekViewEntity.Style.Builder()
            .setBackgroundColor(getColor(actCtx, event.courseId))
            .setTextColorResource(R.color.scheduleOtherText)
            .build()

        // Build the WeekViewEvent via the Builder
        return WeekViewEntity.Event.Builder(this)
            .setId(event.getHash().toLong())
            .setTitle(event.title)
            .setStartTime(Calendar.getInstance().apply { timeInMillis = event.startDateTime })
            .setEndTime(Calendar.getInstance().apply { timeInMillis = event.startDateTime + event.duration })
            .setSubtitle(event.location ?: "")
            .setAllDay(false)
            .setStyle(style)
            .build()
    }
}

class MyCustomPagingAdapter(
    private val loadMoreHandler: LoadMoreHandler
) : WeekView.PagingAdapter<ContextEvent>() {

    interface LoadMoreHandler {
        fun setOnLoadMoreListener(startDate: Calendar, endDate: Calendar)
        fun setOnRangeChangeListener(firstVisibleDate: Calendar, lastVisibleDate: Calendar)
        fun setOnEventClickListener(data: ContextEvent)
    }

    override fun onCreateEntity(item: ContextEvent): WeekViewEntity {
        return item.toWeekViewEvent()
    }

    override fun onLoadMore(startDate: Calendar, endDate: Calendar) {
        loadMoreHandler.setOnLoadMoreListener(startDate, endDate)
    }

    override fun onRangeChanged(firstVisibleDate: Calendar, lastVisibleDate: Calendar) {
        loadMoreHandler.setOnRangeChangeListener(firstVisibleDate, lastVisibleDate)
    }

    override fun onEventClick(data: ContextEvent) {
        loadMoreHandler.setOnEventClickListener(data)
    }

    fun updateEntries(elements: List<ContextEvent>) {
        this.submitList(elements)
        this.refresh()
    }
}
