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

package xyz.apiote.bimba.czwek.repo

import xyz.apiote.bimba.czwek.api.structs.FeedInfoV1
import xyz.apiote.bimba.czwek.api.structs.FeedInfoV2
import xyz.apiote.bimba.czwek.api.structs.QrLocationV1
import xyz.apiote.fruchtfleisch.Reader
import xyz.apiote.fruchtfleisch.Writer
import java.io.InputStream
import java.io.OutputStream
import java.time.LocalDate
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
import java.util.Locale

class FeedInfoPrev {
	companion object {
		fun unmarshal(@Suppress("UNUSED_PARAMETER") stream: InputStream): FeedInfo {
			return FeedInfo(FeedInfoPrev())
		}
	}
}

data class FeedInfo(
	val id: String,
	val name: String,
	val attribution: String,
	val description: String,
	val lastUpdate: LocalDate,
	val qrHost: String,
	val qrIn: QrLocation,
	val qrSelector: String,
	val validSince: LocalDate?,
	val validTill: LocalDate?,
	val cached: Boolean
) {
	companion object {
		const val VERSION = 100u
		private fun parseDate(dateString: String) =
			LocalDate.parse(dateString, DateTimeFormatter.ISO_LOCAL_DATE)


		fun unmarshal(stream: InputStream): FeedInfo {
			val reader = Reader(stream)
			val id = reader.readString()
			val name = reader.readString()
			val attribution = reader.readString()
			val description = reader.readString()
			val lastUpdate = parseDate(reader.readString())
			val qrHost = reader.readString()
			val qrIn = QrLocation.of(QrLocationV1.of(reader.readUInt().toULong().toUInt()))
			val qrSelector = reader.readString()
			val validSince = reader.readString()
			val validTill = reader.readString()

			return FeedInfo(
				id,
				name,
				attribution,
				description,
				lastUpdate,
				qrHost,
				qrIn,
				qrSelector,
				if (validSince != "") parseDate(validSince) else null,
				if (validTill != "") parseDate(validTill) else null,
				true
			)
		}
	}

	constructor(f: FeedInfoV2, cached: Boolean = false) : this(
		f.id,
		f.name,
		f.attribution,
		f.description,
		f.lastUpdate,
		f.qrHost,
		QrLocation.of(f.qrIn),
		f.qrSelector,
		f.validSince,
		f.validTill,
		cached
	)

	constructor(f: FeedInfoV1, cached: Boolean = false) : this(
		f.id,
		f.name,
		f.attribution,
		f.description,
		f.lastUpdate.toLocalDate(),
		"",
		QrLocation.UNKNOWN,
		"",
		null,
		null,
		cached
	)

	constructor(@Suppress("UNUSED_PARAMETER") f: FeedInfoPrev) : this(
		"",
		"",
		"",
		"",
		LocalDate.MIN,
		"",
		QrLocation.UNKNOWN,
		"",
		null,
		null,
		false
	)

	fun marshal(stream: OutputStream) {
		val writer = Writer(stream)
		writer.writeString(id)
		writer.writeString(name)
		writer.writeString(attribution)
		writer.writeString(description)
		writer.writeString(formatDateMarshal(lastUpdate))
		writer.writeString(qrHost)
		writer.writeUInt(qrIn.value().toULong())
		writer.writeString(qrSelector)
		writer.writeString(if (validSince == null) "" else formatDateMarshal(validSince))
		writer.writeString(if (validTill == null) "" else formatDateMarshal(validTill))
	}

	private fun formatDateMarshal(date: LocalDate): String {
		return date.format(DateTimeFormatter.ISO_LOCAL_DATE)
	}

	fun formatDate(): String {
		return lastUpdate.format(
			DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL).withLocale(Locale.getDefault())
		)
	}
}

fun FeedInfo?.join(other: FeedInfo?): FeedInfo {
	assert(this != null || other != null)

	if (this == null) return other!!
	if (other == null) return this

	return FeedInfo(
		id,
		other.name,
		other.attribution,
		other.description,
		if (lastUpdate.isAfter(other.lastUpdate)) lastUpdate else other.lastUpdate,
		other.qrHost,
		other.qrIn,
		other.qrSelector,
		if (other.validSince == null || (validSince
				?: LocalDate.MIN).isAfter(other.validSince)
		) validSince else other.validSince,
		if (other.validTill == null || (validTill
				?: LocalDate.MIN).isAfter(other.validTill)
		) validTill else other.validTill,
		this.cached && other.cached
	)
}
