package alraune

import alraune.entity.*
import pieces100.*
import vgrechka.*
import wasing.OoWasingLandingPage
import java.sql.Timestamp
import java.util.*
import kotlin.properties.ReadOnlyProperty
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
import kotlin.reflect.KProperty0
import kotlin.reflect.KProperty1

fun redirect(path: String): Nothing {
    rctx0.commands.clear()
    btfSetLocationHref(path)
    spitFreakingPage {}
    throw ShitServed()
}

fun spitWindowOpenPage(href: String) {
    btfEval("window.open(${jsStringLiteral(href)})")
    spitUsualPage {div()}
}

fun redirectToLandingIfSiteIsNot(site: AlSite) {
    if (rctx0.al.site() != site)
        redirect(landingPagePath())
}

fun landingPagePath() = when (rctx0.al.site()) {
    AlSite.BoloneCustomer -> OoCustomerLandingPage.path
    AlSite.BoloneWriter -> OoWriterLandingPage.path
    AlSite.BoloneAdmin -> OoAdminLandingPage.path
    AlSite.Wasing -> OoWasingLandingPage.path
}

fun bailOutToLittleErrorPage(content: String): Nothing {
    bailOutToLittleErrorPage(span(content))
}

fun bailOutToLittleErrorPage_boldArg(template: String, arg: Any?): Nothing {
    val html = escapeHtml(template).replace("%s", when {
        arg == null || arg is String && arg.isBlank() ->
            "<i style='color: ${Color.Gray700};'>${t("blank", "пусто")}</i>"
        else -> "<b>" + escapeHtml(arg.toString()) + "</b>"
    })
    bailOutToLittleErrorPage(rawHtml(html))
}

fun bailOutToLittleErrorPage(content: Renderable): Nothing {
    cancelAllCommandsAndSpitLittleErrorPage(content)
    throw ShitServed()
}

fun cancelAllCommandsAndSpitLittleErrorPage(content: Renderable) {
    check(!rctx0.isPost)

    rctx0.commands.clear()
    spitFreakingPage {
        oo(content)
    }
}

fun largeUUID() = UUID.randomUUID().toString() // TODO:vgrechka Make it larger?

fun <T> relateStack(stackCapture: StackCaptureException, f: () -> T): T {
    if (!alConfig.debug.enabled.relateStack) return f()

    AlGlobal_MergeMeToKotlinVersion.relatedStacks.get().addLast(stackCapture)
    try {
        return f()
    } finally {
        AlGlobal_MergeMeToKotlinVersion.relatedStacks.get().removeLast()
    }
}



annotation class LeJson(val withTypeInfo: Boolean = false)

annotation class LeXStream

interface WithUUID {
    val uuid: String
}

fun isLeJsonProperty(prop: KProperty1<Any, Any?>) =
    findLeJsonAnnotation(prop) != null

fun findLeJsonAnnotation(prop: KProperty1<*, *>) =
    prop.annotations.find {it is LeJson} as LeJson?

fun findLeXStreamAnnotation(prop: KProperty1<*, *>) =
    prop.annotations.find {it is LeXStream} as LeXStream?

data class FAIconWithStyle(
    val icon: FA.Icon,
    val style: String = "",
    val nonFirstItemClass: AlCSS.Pack = AlCSS.fartus1.p1BlueGray,
    val amendTitleBar: (AlTag) -> Unit = {})

fun checkAtMostOneNotNull(vararg props: KProperty0<*>) {
    val notNulls = props.filter {it.get() != null}
    if (notNulls.size > 1)
        wtf("[${::checkAtMostOneNotNull.name}] notNulls = [${notNulls.joinToString(", ") {it.name}}]")
}

fun checkExactlyOneNotNull(vararg props: KProperty0<*>) {
    val notNulls = props.filter {it.get() != null}
    if (notNulls.size != 1)
        wtf("[${::checkExactlyOneNotNull.name}] notNulls = [${notNulls.joinToString(", ") {it.name}}]")
}

fun checkAtLeastOneNotNull(vararg props: KProperty0<*>) {
    val notNulls = props.filter {it.get() != null}
    if (notNulls.isEmpty())
        wtf(::checkAtLeastOneNotNull.name)
}



fun <T> fqnamed(make: (List<String>) -> T) = object : ReadOnlyProperty<Any?, T> {
    private var value: T? = null

    override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        if (value == null) {
            val clazz = thisRef!!::class
            val packageName = clazz.java.`package`.name
            var className = clazz.qualifiedName!!
            className = className.substring(packageName.length + 1)
            val pieces = className.split(".")

            value = make(pieces + listOf(property.name))
        }
        return bang(value)
    }
}

fun myFqname_under() = fqnamed {it.joinToString("_")}


fun <T : Any> getParamOrBailOut(param: AlBunchOfGetParams.Param<T>): T =
    param.get()
        ?: bailOutToLittleErrorPage(rawHtml(t("TOTE", "Я хочу в URL параметр <b>${param.name}</b>")))

interface GenericOperationInfoProps {
    val operationId: Long
    val actorDescr: String
    val title: String
    val time: Timestamp
    val timeString: String
    val userIcon: FA.Icon
    fun hasStateBefore(): Boolean
}

//sealed class OperationInfo : GenericOperationInfoProps {
//    class Order_V1(
//        override val operationId: Long,
//        val data: AlOperation_Order_V1,
//        override val actorDescr: String,
//        override val title: String,
//        override val time: Timestamp,
//        override val timeString: String,
//        override val userIcon: FA.Icon) : OperationInfo() {
//
//        fun idAndTime() = AlText.numString(operationId) + ", " + timeString
//        override fun hasStateBefore() = data.orderBeforeOperation != null
//    }
//}



//fun drooOrderParamsComparison(entity1: AlUAOrder?, entity2: AlUAOrder) {
//    oo(div().with {
//        // Children are counted in CSS (nth-child stuff) relative to this div
//        fun drooPropComparisons(obj1: Any?, obj2: Any, exclude: List<KProperty1<*, *>> = listOf()) {
//            val props = (obj2::class.memberProperties - exclude)
//            for (prop in props) {
//                ooPropComparison(PropComparisonParams(prop), obj1, obj2)
//            }
//        }
//
//        drooPropComparisons(entity1, entity2, exclude = listOf(
//            AlUAOrder::_id, AlUAOrder::createdAt,
//            AlUAOrder::updatedAt, AlUAOrder::deleted, AlUAOrder::dataPile))
//        drooPropComparisons(entity1?.dataPile, entity2.dataPile)
//    })
//}

fun isNullOrBlankString(x: Any?) = x == null || x is String && x.isBlank()

//fun <T> T.fluent(block: () -> Unit): T {
//    block()
//    return this
//}

enum class Rightness {
    Left, Right;

    fun cssString(): String = name.toLowerCase()
}

interface Titled {
    val title: String
}

interface WithId {
    val id: Long
}

interface WithOptimisticVersion {
    var optimisticVersion: String
}

interface WithMutableId: WithId {
    override var id: Long
}

fun isWriterAssigned(order: Order) = false

interface EncryptedDownloadableResource {
    var name: String
    var size: Int
    var downloadUrl: String
    var secretKeyBase64: String
}




















