/*
 *   This file is a part of the SteamWar software.
 *
 *   Copyright (C) 2025 SteamWar.de-Serverteam
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

package de.steamwar.sql

import de.steamwar.sql.internal.useDb
import org.jetbrains.exposed.v1.core.SortOrder
import org.jetbrains.exposed.v1.core.dao.id.CompositeID
import org.jetbrains.exposed.v1.core.dao.id.CompositeIdTable
import org.jetbrains.exposed.v1.core.dao.id.EntityID
import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.core.statements.api.ExposedBlob
import org.jetbrains.exposed.v1.dao.CompositeEntity
import org.jetbrains.exposed.v1.dao.CompositeEntityClass
import org.jetbrains.exposed.v1.javatime.CurrentTimestamp
import org.jetbrains.exposed.v1.javatime.timestamp
import org.jetbrains.exposed.v1.jdbc.insert
import java.io.InputStream
import java.util.zip.GZIPInputStream

object NodeDataTable : CompositeIdTable("NodeData") {
    val nodeId = reference("NodeId", SchematicNodeTable).index()
    val createdAt = timestamp("CreatedAt").defaultExpression(CurrentTimestamp).entityId()
    val nodeFormat = enumeration("NodeFormat", NodeData.SchematicFormat::class)
    val schemData = blob("SchemData")

    override val primaryKey = PrimaryKey(nodeId, createdAt)

    init {
        addIdColumn(nodeId)
    }
}

class NodeData(id: EntityID<CompositeID>) : CompositeEntity(id) {
    val nodeId by NodeDataTable.nodeId
    val createdAt by NodeDataTable.createdAt
    val nodeFormat by NodeDataTable.nodeFormat
    val schemData by NodeDataTable.schemData

    companion object : CompositeEntityClass<NodeData>(NodeDataTable) {
        @JvmStatic
        fun getLatest(node: SchematicNode) =
            useDb {
                find { (NodeDataTable.nodeId eq node.nodeId) }.orderBy(NodeDataTable.createdAt to SortOrder.DESC).firstOrNull()
            }

        @JvmStatic
        fun get(node: SchematicNode) =
            useDb {
                find { (NodeDataTable.nodeId eq node.nodeId) }.orderBy(NodeDataTable.createdAt to SortOrder.ASC).toList()
            }

        @JvmStatic
        fun get(node: SchematicNode, revision: Int) =
            useDb {
                find { NodeDataTable.nodeId eq node.nodeId }.orderBy(NodeDataTable.createdAt to SortOrder.ASC).toList().get(revision - 1)
            }

        @JvmStatic
        fun getRevisions(node: SchematicNode) =
            useDb {
                count(NodeDataTable.nodeId eq node.nodeId).toInt()
            }

        @JvmStatic
        fun saveFromStream(node: SchematicNode, blob: InputStream, format: SchematicFormat) =
            useDb {
                NodeDataTable.insert {
                    it[NodeDataTable.nodeId] = EntityID(node.getId(), SchematicNodeTable)
                    it[NodeDataTable.nodeFormat] = format
                    it[NodeDataTable.schemData] = ExposedBlob(blob.readBytes())
                }
            }
    }

    fun schemData(decompress: Boolean) =
        useDb {
            schemData.inputStream.let { if (decompress) GZIPInputStream(it) else it }
        }

    override fun delete() =
        useDb { super.delete() }

    enum class SchematicFormat(val fileEnding: String) {
        MCEDIT(".schematic"),
        SPONGE_V2(".schem"),
        SPONGE_V3(".schem");
    }
}