package alraune

import pieces100.*
import vgrechka.*
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
import alraune.Al.*
import alraune.Gender.*
import alraune.entity.*
import com.fasterxml.jackson.annotation.JsonIgnore
import java.sql.Timestamp
import kotlin.reflect.*


fun ooInsideIgnoreWhenCheckingForStaleness_2(block: () -> Unit) {
    oo(rawHtml("<!--${BTFConst.begin_ignoreWhenCheckingForStaleness_2}-->"))
    block()
    oo(rawHtml("<!--${BTFConst.end_ignoreWhenCheckingForStaleness_2}-->"))
}

class StyleSheet {
    val classNamePrefix = AlGlobal.nextPuid()
    var classNameSuffix = 1
    val packs = mutableListOf<AlCSS.Pack>()

    inner class DeClass(val pack: AlCSS.Pack) {
        constructor(style: String) : this(AlCSS.Pack(style))

        operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>): ReadOnlyProperty<Any?, String> {
            val value = addClass(pack, nameSuffix = prop.name)
            return object : ReadOnlyProperty<Any?, String> {
                override fun getValue(thisRef: Any?, property: KProperty<*>) = value
            }
        }
    }

    fun addClass(pack: AlCSS.Pack, nameSuffix: String? = null): String {
        val className = "$classNamePrefix--${nameSuffix ?: classNameSuffix++}"
        pack.autoClassName = className
        packs += pack
        return className
    }

    fun compose(): Renderable {
        val shit = stringBuild {s->
            packs.forEach {it.renderInto(s)}
        }
        return rawHtml("<style>$shit</style>")
    }
}

fun drooOrderShit(order: AlUAOrder) {
    oo(Row().with {
        oo(composeCreatedAtCol(3, order.createdAt))
        oo(composeOrderStateColumn(order))
        oo(composeTimestampCol(3, t("TOTE", "Срок"), order.dataPile.deadline))
    })

    oo(Row().with {
        oo(col(3, AlText.Order.contactName, order.contactName))
        oo(col(3, AlText.Order.email, order.email))
        oo(col(3, AlText.Order.phone, order.phone))
    })

    oo(Row().with {
        oo(composeOrderDocumentTypeColumn(order))
        oo(composeOrderDocumentCategoryColumn(order))
    })

    oo(composeOrderNumsRow(order))

    oo(DetailsRow().content(order.documentDetails))

    AlRender2.drawAdminNotes_ifAny_andPretendingAdmin(order.adminNotes)
}

class ooItemTitle(title: String,
                  tinySubtitle: String,
                  addIcons: (AddIcons) -> Unit = {},
                  buildDropdown: ((MenuBuilder) -> Unit)? = null,
                  val iconWithStyle: FAIconWithStyle? = null,
                  val icon: FA.Icon? = null,
                  val amendTitleBarDiv: (AlTag) -> Unit = {},
                  val afterTinySubtitle: () -> Unit = {},
                  val belowTitle: Renderable? = null) {
    val c = AlCSS.carla
    val rightIconsContainer = div()

    inner class AddIcons {
        fun item(icon: FA.Icon, hint: String): AlTag {
            val tag = tagi().className(icon.className + " " + c.titleRightIcon).title(hint)
            oo(tag)
            return tag
        }
    }

    init {
        checkAtMostOneNotNull(this::iconWithStyle, this::icon)
        val _iconAndStyle = when {
            iconWithStyle != null -> iconWithStyle
            icon != null -> FAIconWithStyle(icon)
            else -> FAIconWithStyle(FA.file)
        }

        oo(div().className(c.titleBar).also(amendTitleBarDiv).also{_iconAndStyle.amendTitleBar(it)}.with {
            oo(div().with {
//                oo(tagi().classes(_iconAndStyle.icon.className, c.titleIcon).style(_iconAndStyle.style))
                val titleIcon = FA.file()
                if (titleIcon != null)
                    oo(span().classes(c.titleIcon).add(titleIcon))

                oo(span().classes(c.title).add(title))
                oo(span().classes(c.tinySubtitle).text(tinySubtitle))
                afterTinySubtitle()
                oo(rightIconsContainer.classes(c.titleRightIcons).with {
                    addIcons(AddIcons())
                    if (buildDropdown != null)
                        ooHamburgerDropdown(buildDropdown).appendClassName(c.titleRightIcon)
                })
            })
            oo(belowTitle)
        })
    }
}

fun ooHamburgerDropdown(buildDropdown: (MenuBuilder) -> Unit): AlTag {
    val tagi = tagi().classes(FA.bars.className)
    ooDropdown(tagi, block = buildDropdown)
    return tagi
}

fun ooButtonDropdown(title: String, buildDropdown: (MenuBuilder) -> Unit): AlTag {
    val trigger = button().classes("btn", "btn-default").with {
        oo(span(title))
        oo(span().classes("caret").marginLeft05Em())
    }
    ooDropdown(trigger, block = buildDropdown)
    return trigger
}

fun AlTag.transparent_noPadding_noBorder() = this.also {
    it.appendStyle("padding: 0; background: transparent; border: none;")
}

fun shitOrderById(q: AlQueryBuilder, ordering: Ordering) {
    q.text("order by id " + when (ordering) {
        Ordering.NewerFirst -> "desc"
        Ordering.OlderFirst -> "asc"
    })
}

abstract class OoUsualPaginatedShit<Item : Any>(
    val itemClass: KClass<Item>,
    val table: String,
    val pageSize: Int = AlConst.pageSize,
    var ordering: Ordering = GetParams().ordering.get()!!,
//    val url: AmendableUrlProvider,
    val notDeleted: Boolean = true,
    val pizda: Pizda = Pizda())
    : DancerBase<Renderable>() {

    class Pizda(
        val composeBeforeItems: () -> Renderable? = {null},
        val composeAfterItems: () -> Renderable? = {null})

    open fun initShitGivenItems(items: List<Item>) {}
    abstract fun composeItem(item: Item, index: Int): Renderable
    abstract fun shitIntoWhere(q: AlQueryBuilder)

    open fun shitOrderBy(q: AlQueryBuilder) {shitOrderById(q, ordering)}

    override fun dance(): Renderable {
        val crap = UsualPaginationPussy(itemClass, pageSize, table, notDeleted, ::shitIntoWhere, ::shitOrderBy)

        return dance(object : ComposePaginatedShit<Item>(crap) {
            init {pizda = this@OoUsualPaginatedShit.pizda}

            override fun initShitGivenItems(items: List<Item>) = this@OoUsualPaginatedShit.initShitGivenItems(items)
            override fun renderItem(item: Item, index: Int) = this@OoUsualPaginatedShit.composeItem(item, index)

            override fun makePageHref(pageFrom1: Int) = defaultPaginationPageHref(pageFrom1)
        })
    }
}

fun defaultPaginationPageHref(pageFrom1: Int): String {
    return makeUrlBasedOnCurrent {
        it.page.set(pageFrom1)
    }
}

interface PaginationPussy<Item> {
    fun count(): Long
    fun selectItems(offset: Long): List<Item>
    fun pageSize(): Int
}

class UsualPaginationPussy<Item : Any>(
    val itemClass: KClass<Item>,
    val pageSize: Int = AlConst.pageSize,
    val table: String,
    val notDeleted: Boolean = false,
    val shitIntoWhere: (AlQueryBuilder) -> Unit,
    val shitOrderBy: (AlQueryBuilder) -> Unit)
    :PaginationPussy<Item> {

    override fun pageSize() = pageSize

    override fun selectItems(offset: Long): List<Item> {
        return AlQueryBuilder()
            .text("select * from $table")
            .text("where true")
            .text(notDeleted.thenElseEmpty {"and not deleted"})
            .accept {shitIntoWhere(it)}
            .accept {shitOrderBy(it)}
            .text("limit").param(pageSize)
            .text("offset").param(offset)
            .selectSimple(itemClass.java)
    }

    override fun count() = AlQueryBuilder()
        .text("select count(*) from $table")
        .text("where true")
        .text(notDeleted.thenElseEmpty {"and not deleted"})
        .accept {shitIntoWhere(it)}
        .selectOneLong()
}

class PaginationBoobs<Item>(@JsonIgnore val pussy: PaginationPussy<Item>) {
    var items by notNullOnce<List<Item>>()
    var pages by notNullOnce<Int>()
    var page by notNull<Int>()
    var count by notNullOnce<Long>()

    fun fart(_page: Int? = null) {
        count = pussy.count()
        pages = Math.toIntExact(
            count / pussy.pageSize()
                + (if ((count % pussy.pageSize()) > 0) 1 else 0))

        page = _page ?: GetParams().page.get()!!

        if (page < 1)
            page = 1
        else if (page > pages)
            page = if (pages > 0) pages else 1

        val offset = (page - 1) * pussy.pageSize()
        items = pussy.selectItems(offset.toLong())
    }
}


class ShitIntoPageHeader(val block: () -> Unit)


class DrooSelect(val jsOnChange: (jsValue: String) -> String, val style: String? = null) {
    fun data(items: List<TitledValue_killme>, currentValue: String) = object : DancerBase<Unit>() {
        override fun dance() {
            val selectDomid by nextJsIdentDel()
            val jsValue = "${jsByIdSingle(selectDomid)}.val()"
            oo(select().id(selectDomid).style(style).className("form-control").onChange(jsOnChange(jsValue)).with {
                for (item in items) {
                    oo(option()
                        .selected(currentValue == item.value)
                        .value(item.value)
                        .text(item.title))
                }
            })
        }
    }

    fun <E> data(items: Array<E>, currentValue: E)
        where E : Enum<E>, E : JTitled =
        data(items = items.map {TitledValue_killme(it.name, it.title)},
            currentValue = currentValue.name)

}

fun spitPageWithUsualShit(w: Params_spitPageWithUsualShit) {
    spitUsualPage {div().className("container").with {
        addPageHeaderWithControls(w.getHeaderTitle(), w::buildTopRightControls)
        w.drawBody()
    }}
}

interface Params_spitPageWithUsualShit {
    fun drawBody()
    fun getHeaderTitle(): String
    fun buildTopRightControls(trb: TopRightControls)
}

fun spitTitledPage(title: String, build: () -> Unit) {
    rctx0.htmlHeadTitle = title
    spitUsualPage {div().className("container").with {
        oo(pageHeader(title))
        build()
    }}
}

class Col(val size: Int) : RenderableRelatingItsCreationStackToRendering() {
    private var title: String? = null; fun title(x: String): Col {this.title = x; return this;}
    private var colClassName = ""; fun colClassName(x: String): Col {this.colClassName = x; return this;}
    private var labelStyle = ""; fun labelStyle(x: String): Col {this.labelStyle = x; return this;}
    private var contentClassName = ""; fun contentClassName(x: String): Col {this.contentClassName = x; return this;}
    private var contentStyle = ""; fun contentStyle(x: String): Col {this.contentStyle = x; return this;}
    private var with: (() -> Unit)? = null; fun with(x: () -> Unit): Col {this.with = x; return this;}

    fun amend(f: ((Col) -> Unit)?): Col {
        if (f != null) f(this)
        return this
    }

    override fun compose(): Renderable {
        return div().className("col-md-" + size).with {
            if (title != null)
                oo(label().style(labelStyle + "margin-bottom: 0;").text(title))
            oo(div().className(contentClassName).style(contentStyle).with(with))
        }
    }
}

fun col(size: Int, content: Renderable): Col {
    return Col(size).with {oo(content)}
}

fun col(size: Int, title: String, content: String): Col {
    return Col(size).title(title).with {oo(span(content))}
}

fun composeMinimalMainContainer(build: () -> Unit): Renderable {
    rctx0.compositionPlace.belowRootContainer = div()
    return div().with {
        oo(div().id(BTFConst.Domid.mainContainer).className("container").with {
            oo(div().style("margin-top: 1rem;"))
            build()
        })
        oo(rctx0.compositionPlace.belowRootContainer)
    }
}

// 2bd38503-ee2b-41e7-b5a6-97cbdde8dfd8
fun composeWaitingBanner(text: String): AlTag {
    val waitingAdminApprovalBannerStyle = """
        background-color: ${Color.Gray100};
        border-left: 3px solid ${Color.Gray500};
        height: 44.4px;
        margin-bottom: 1rem;
        padding-left: 1rem;
        padding-right: 0;
        padding-top: 0.5rem;
        padding-bottom: 0.5rem;
        display: flex;
        align-items: center;
    """

    return div().style(waitingAdminApprovalBannerStyle).with {
        oo(tagi().className(FA.hourglassHalf.className).style("margin-right: 0.75rem;"))
        oo(div().style("flex-grow: 1;").text(text))
    }
}

fun composeCreatedAtCol(size: Int, value: Timestamp, gender: Gender = Masculine) =
    composeTimestampCol(size, t(
        "Created",
        when (gender) {
            Masculine -> "Создан"
            Feminine -> "Создана"
            Neuter -> "Создано"
        }), value)

fun composeTimestampCol(size: Int, title: String, value: Timestamp) =
    col(size, title, TimePile.kievTimeString(value))

fun composeOrderDocumentTypeColumn(order: AlUAOrder) =
    col(3, AlText.documentType, order.documentType.title)

fun composeOrderDocumentTypeColumn_a2(order: Order) =
    col(3, AlText.documentType, order.data.documentType.title)

fun composeOrderDocumentCategoryColumn(order: AlUAOrder) =
    col(6, AlText.documentCategory, longDocumentCategoryTitle(order.documentCategory))

fun composeOrderDocumentCategoryColumn_a2(order: Order) =
    col(6, AlText.documentCategory, longDocumentCategoryTitle(order.data.documentCategory))

fun longDocumentCategoryTitle(categoryID: String): String {
    var cat = AlUADocumentCategories.findByIDOrBitch(categoryID)
    val steps = mutableListOf<String>()
    while (!cat.id.equals(AlUADocumentCategories.root.id)) {
        steps.add(cat.title)
        cat = bang(cat.parent)
    }
    steps.reverse()
    return steps.joinToString(AlText0.rightDoubleAngleQuotationSpaced)
}


fun composeOrderStateColumn(order: AlUAOrder) =
    col(3, AlText.status, order.state.title)

fun composeOrderStateColumn_a2(order: Order) =
    col(3, AlText.status, order.state.title)

fun composeOrderNumSourcesColumn(order: AlUAOrder) =
    col(3, AlText.Order.numSources, "" + order.numSources)

fun composeOrderNumSourcesColumn_a2(order: Order) =
    col(3, AlText.Order.numSources, "" + order.data.numSources)

fun composeOrderNumPagesColumn(order: AlUAOrder) =
    col(3, AlText.Order.numPages, "" + order.numPages)

fun composeOrderNumPagesColumn_a2(order: Order) =
    col(3, AlText.Order.numPages, "" + order.data.numPages)

class Row : RenderableRelatingItsCreationStackToRendering() {
    private var marginBottom = "0.5em"; fun marginBottom(x: String): Row {this.marginBottom = x; return this;}
    private var additionalClassNames = ""; fun appendClassName(x: String): Row {additionalClassNames += " " + x; return this;}
    private var with: (() -> Unit)? = null; fun with(x: () -> Unit): Row {this.with = x; return this;}

    override fun compose(): Renderable {
        var style = ""
        if (marginBottom.isNotBlank())
            style += "margin-bottom: $marginBottom;"

        return div().className("row $additionalClassNames").style(style).with(with)
    }
}

fun composeOrderNumsRow(order: AlUAOrder) = Row().with {
    oo(composeOrderNumPagesColumn(order))
    oo(composeOrderNumSourcesColumn(order))
}

fun composeOrderNumsRow_a2(order: Order) = Row().with {
    oo(composeOrderNumPagesColumn_a2(order))
    oo(composeOrderNumSourcesColumn_a2(order))
}


fun AlTag.render(block: () -> Unit): String = this.with(block).render()

fun AlTag.displayNone() = this.also {this.appendStyle("display: none;")}!!




fun composeOrderParams(order: Order) = div().with {
    val data = order.data
    oo(Row().with {
        oo(composeCreatedAtCol(3, data.createdAt))
        ooUpdatedAtColIfDiffersFromCreatedAt(order.data.updatedAt, data.createdAt)
        oo(col(3, AlText.status, order.state.title))
        oo(composeTimestampCol(3, t("TOTE", "Срок"), order.deadline))
    })

//    if (rctx.pretending == AlUserKind.Admin) {
//        data.bidding?.let {bidding->
//            oo(Row().with {
//                oo(col(3, t("TOTE", "Нижняя граница ставки"), displayMoney(bidding.minWriterMoney)))
//                oo(col(3, t("TOTE", "Верхняя граница ставки"), displayMoney(bidding.maxWriterMoney)))
//                oo(composeTimestampCol(3, t("TOTE", "Напомнить закрыть торги"), bidding.closeBiddingAdviceNotificationTime))
//            })
//        }
//    }

    if (Context1.get().projectStuff.composeOrderParams_showCustomerInfo) {
        oo(Row().with {
            oo(col(3, AlText.Order.contactName, data.contactName))
            oo(col(3, AlText.Order.email, data.email))
            oo(col(3, AlText.Order.phone, data.phone))
        })
    }

    oo(Row().with {
        oo(col(3, AlText.documentType, data.documentType.title))
        oo(col(6, AlText.documentCategory, longDocumentCategoryTitle(data.documentCategory)))
    })

    oo(Row().with {
        oo(col(3, AlText.Order.numPages, "" + data.numPages))
        oo(col(3, AlText.Order.numSources, "" + data.numSources))
    })

    oo(DetailsRow().content(data.documentDetails))

    AlRender2.drawAdminNotes_ifAny_andPretendingAdmin(data.adminNotes)
}

fun ooUpdatedAtColIfDiffersFromCreatedAt(updatedAt: Timestamp, createdAt: Timestamp, gender: Gender = Masculine) {
    if (updatedAt != createdAt)
        oo(composeTimestampCol(3, t(
            "Updated",
            when (gender) {
                Masculine -> "Обновлен"
                Feminine -> "Обновлена"
                Neuter -> "Обновлено"
            }), updatedAt))
}

























