// SPDX-FileCopyrightText: Adam Evyčędo
//
// SPDX-License-Identifier: GPL-3.0-or-later

package xyz.apiote.bimba.czwek.dashboard.ui.home

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import xyz.apiote.bimba.czwek.R
import xyz.apiote.bimba.czwek.departures.DeparturesActivity
import xyz.apiote.bimba.czwek.repo.Event
import xyz.apiote.bimba.czwek.repo.Favourite
import java.time.ZoneId
import java.time.ZonedDateTime
import java.util.Collections
import java.util.Optional

class BimbaFavouritesAdapter(
	private var favourites: List<Favourite>,
	private var departures: Map<String, Optional<Event>>,
	private val inflater: LayoutInflater,
	private val context: Context
) :
	RecyclerView.Adapter<FavouriteViewHolder>() {
	var lastUpdate: ZonedDateTime =
		ZonedDateTime.of(0, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault())
		private set

	inner class DiffUtilCallback(
		private val oldFavourites: List<Favourite>,
		private val oldDepartures: Map<String, Optional<Event>?>,
		private val newFavourites: List<Favourite>,
		private val newDepartures: Map<String, Optional<Event>?>
	) : DiffUtil.Callback() {
		override fun getOldListSize() = oldFavourites.size

		override fun getNewListSize() = newFavourites.size

		override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) =
			oldFavourites[oldItemPosition].feedID + oldFavourites[oldItemPosition].stopCode == newFavourites[newItemPosition].feedID + newFavourites[newItemPosition].stopCode

		@Suppress("KotlinConstantConditions", "UNNECESSARY_NOT_NULL_ASSERTION")
		override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
			val oldFav = oldFavourites[oldItemPosition]
			val newFav = newFavourites[newItemPosition]
			val oldDeparture = oldDepartures[oldFav.feedID + oldFav.stopCode]
			val newDeparture = newDepartures[oldFav.feedID + oldFav.stopCode]

			if ((oldDeparture == null && newDeparture != null) || (oldDeparture != null && newDeparture == null)) {
				return false
			}

			val favouritesSame = oldFav.feedName == newFav.feedName &&
					oldFav.stopName == newFav.stopName &&
					oldFav.sequence == newFav.sequence &&
					oldFav.lines == newFav.lines

			if (!favouritesSame) {
				return false
			}

			if ((oldDeparture == null && newDeparture == null) || (oldDeparture!!.isEmpty && newDeparture!!.isEmpty)) {
				return true
			}

			if ((oldDeparture!!.isEmpty && !newDeparture!!.isEmpty) || (!oldDeparture!!.isEmpty && newDeparture!!.isEmpty)) {
				return false
			}

			return oldDeparture!!.get().id == newDeparture!!.get().id &&
					oldDeparture!!.get().vehicle.Line == newDeparture!!.get().vehicle.Line &&
					oldDeparture!!.get().vehicle.Headsign == newDeparture!!.get().vehicle.Headsign &&
					oldDeparture!!.get().statusText(
						context,
						false,
						lastUpdate
					) == newDeparture!!.get().statusText(context, false)
		}
	}

	override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FavouriteViewHolder {
		val rowView = inflater.inflate(R.layout.favourite, parent, false)
		return FavouriteViewHolder(rowView)
	}

	override fun getItemCount() = favourites.size

	override fun onBindViewHolder(holder: FavouriteViewHolder, position: Int) {
		FavouriteViewHolder.bind(
			favourites[position], holder, context,
			departures[favourites[position].feedID + favourites[position].stopCode]
		)
	}

	fun updateFavourites(favourites: List<Favourite>) {
		val diff = DiffUtil.calculateDiff(
			DiffUtilCallback(
				this.favourites,
				this.departures,
				favourites,
				this.departures
			)
		)
		this.favourites = favourites
		diff.dispatchUpdatesTo(this)
	}

	fun updateDepartures(departures: Map<String, Optional<Event>>) {
		this.departures = departures
		notifyDataSetChanged()
		lastUpdate = ZonedDateTime.now()
	}

	fun swap(from: Int, to: Int): List<Favourite> {
		Collections.swap(favourites, from, to)
		favourites = favourites.mapIndexed { i, it ->
			it.copy(sequence = i)
		}
		notifyItemMoved(from, to)
		return favourites
	}

	fun delete(position: Int): Pair<List<Favourite>, Favourite> {
		val removedFavourite = favourites[position]
		favourites = favourites.filterIndexed { i, _ ->
			i != position
		}.mapIndexed { i, it ->
			it.copy(sequence = i)
		}
		notifyItemRemoved(position)
		return Pair(favourites, removedFavourite)
	}

	fun insert(removedFavourite: Favourite): List<Favourite> {
		favourites = favourites.toMutableList().apply {
			add(removedFavourite.sequence!!, removedFavourite)
		}.mapIndexed { i, it ->
			it.copy(sequence = i)
		}
		return favourites
	}
}

class FavouriteViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
	val root: View = itemView.findViewById(R.id.favourite)
	val feedName: TextView = itemView.findViewById(R.id.feed_name)
	val lineIcon: ImageView = itemView.findViewById(R.id.line_icon)
	val arrivalStatus: TextView = itemView.findViewById(R.id.arrival_status)
	val arrivalTime: TextView = itemView.findViewById(R.id.arrival_time)
	val arrivalTimeFull: TextView = itemView.findViewById(R.id.arrival_full_time)
	val departureStatus: TextView = itemView.findViewById(R.id.departure_status)
	val departureTime: TextView = itemView.findViewById(R.id.departure_time)
	val departureTimeFull: TextView = itemView.findViewById(R.id.departure_full_time)
	val lineName: TextView = itemView.findViewById(R.id.departure_line)
	val headsign: TextView = itemView.findViewById(R.id.departure_headsign)
	val stopHeadline: TextView = itemView.findViewById(R.id.stop_name)

	companion object {
		fun bind(
			favourite: Favourite,
			holder: FavouriteViewHolder,
			context: Context,
			event: Optional<Event>?
		) {
			if (event == null) {
				holder.feedName.text = favourite.feedName
				holder.stopHeadline.text = favourite.stopName
				holder.lineIcon.setImageDrawable(null)
				holder.lineName.text = context.getString(R.string.loading)
				holder.arrivalTime.text = ""
				holder.arrivalTimeFull.text = ""
				holder.headsign.text = ""
			} else if (event.isEmpty) {
				holder.feedName.text = favourite.feedName
				holder.stopHeadline.text = favourite.stopName
				holder.lineIcon.setImageDrawable(null)
				holder.lineName.text = context.getString(R.string.no_departures).lowercase()
				holder.arrivalTime.text = ""
				holder.arrivalTimeFull.text = ""
				holder.headsign.text = ""
			} else {
				val statusTexts = event.get().statusText(context, false)
				val vehicle = event.get().vehicle
				holder.feedName.text = favourite.feedName
				holder.stopHeadline.text = favourite.stopName
				holder.lineIcon.setImageDrawable(vehicle.Line.icon(context))
				holder.lineIcon.contentDescription = vehicle.Line.kind.name
				holder.lineName.text = vehicle.Line.name
				holder.headsign.text =
					context.getString(R.string.departure_headsign, vehicle.Headsign)
				holder.headsign.contentDescription =
					context.getString(
						R.string.departure_headsign_content_description,
						vehicle.Headsign
					)

				with(event.get()) {
					if (arrivalTime == departureTime) {
						holder.arrivalStatus.visibility = View.GONE
						holder.arrivalTime.visibility = View.GONE
						holder.arrivalTimeFull.visibility = View.GONE
						holder.departureTime.apply {
							text = statusTexts.second
							visibility = View.VISIBLE
						}
						holder.departureTimeFull.apply {
							text = departureTimeString(context)
							visibility = View.VISIBLE
						}
						if (!exact) {
							holder.departureStatus.apply {
								text = context.getString(R.string.approximately)
								visibility = View.VISIBLE
							}
						} else {
							holder.departureStatus.visibility = View.GONE
						}
					} else {
						if (arrivalTime != null) {
							holder.arrivalTimeFull.apply {
								visibility = View.VISIBLE
								text = arrivalTimeString(context)
							}
							holder.arrivalTime.apply {
								visibility = View.VISIBLE
								text = statusTexts.first
							}
							holder.arrivalStatus.apply {
								visibility = View.VISIBLE
								text = if (!exact) {
									context.getString(R.string.arrival_approximate)
								} else {
									context.getString(R.string.arrival)
								}
							}
						} else {
							holder.arrivalStatus.visibility = View.GONE
							holder.arrivalTime.visibility = View.GONE
							holder.arrivalTimeFull.visibility = View.GONE
						}
						if (departureTime != null) {
							holder.departureTimeFull.apply {
								visibility = View.VISIBLE
								text = departureTimeString(context)
							}
							holder.departureTime.apply {
								visibility = View.VISIBLE
								text = statusTexts.second
							}
							holder.departureStatus.apply {
								visibility = View.VISIBLE
								text = if (!exact) {
									context.getString(R.string.departure_approximate)
								} else {
									context.getString(R.string.departure)
								}
							}
						} else {
							holder.departureStatus.visibility = View.GONE
							holder.departureTime.visibility = View.GONE
							holder.departureTimeFull.visibility = View.GONE
						}
					}
				}
			}

			holder.root.setOnClickListener {
				context.startActivity(
					DeparturesActivity.getIntent(
						context,
						favourite.stopCode,
						favourite.stopName,
						favourite.feedID,
						favourite.lines.toTypedArray()
					)
				)
			}
		}
	}
}