/*
 *   This file is a part of the SteamWar software.
 *
 *   Copyright (C) 2026 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.ReferenceOption
import org.jetbrains.exposed.v1.core.and
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.dao.CompositeEntity
import org.jetbrains.exposed.v1.dao.CompositeEntityClass
import org.jetbrains.exposed.v1.jdbc.insertIgnore
import java.util.*
import kotlin.jvm.optionals.getOrNull

object NodeMemberTable : CompositeIdTable("NodeMember") {
    val node = reference("NodeId", SchematicNodeTable, onDelete = ReferenceOption.CASCADE, onUpdate = ReferenceOption.CASCADE).index()
    val userId = reference("UserId", SteamwarUserTable).index()
    val parentNode = optReference("ParentId", SchematicNodeTable).index()

    override val primaryKey = PrimaryKey(node, userId)

    init {
        addIdColumn(node)
        addIdColumn(userId)
    }
}

class NodeMember(id: EntityID<CompositeID>) : CompositeEntity(id) {
    companion object : CompositeEntityClass<NodeMember>(NodeMemberTable) {
        @JvmStatic
        fun createNodeMember(node: Int, member: Int): NodeMember =
            useDb {
                NodeMemberTable.insertIgnore {
                    it[this.node] = EntityID(node, SchematicNodeTable)
                    it[this.userId] = EntityID(member, SteamwarUserTable)
                }
                getNodeMember(node, member)
                    ?: throw IllegalStateException("NodeMember not created")
            }

        @JvmStatic
        fun createNodeMember(node: Int, member: Int, parent: SchematicNode): NodeMember =
            useDb {
                if (!parent.isDir()) throw IllegalStateException("Parent must be a directory")
                NodeMemberTable.insertIgnore {
                    it[this.node] = EntityID(node, SchematicNodeTable)
                    it[this.userId] = EntityID(member, SteamwarUserTable)
                    it[NodeMemberTable.parentNode] = parent.getId()
                }
                getNodeMember(node, member)
                    ?: throw IllegalStateException("NodeMember not created")
            }

        @JvmStatic
        fun createNodeMember(node: Int, member: SteamwarUser) =
            createNodeMember(node, member.id.value)

        @JvmStatic
        fun getNodeMember(node: Int, member: Int) =
            useDb {
                find { (NodeMemberTable.node eq node) and (NodeMemberTable.userId eq member) }.firstOrNull()
            }

        @JvmStatic
        fun getNodeMember(node: Int, member: SteamwarUser) =
            getNodeMember(node, member.id.value)

        @JvmStatic
        fun getNodeMembers(node: Int) =
            useDb { find { NodeMemberTable.node eq node }.toSet() }

        @JvmStatic
        fun getSchematics(member: Int) =
            useDb { find { NodeMemberTable.userId eq member }.toSet() }

        @JvmStatic
        fun init() =
            Unit
    }

    val node by NodeMemberTable.node.transform({ EntityID(it, SchematicNodeTable) }, { it.value })
    val member by NodeMemberTable.userId.transform({ EntityID(it, SteamwarUserTable) }, { it.value })
    var parent by NodeMemberTable.parentNode.transform(
        { it.map { EntityID(it, SchematicNodeTable) }.getOrNull() },
        { Optional.ofNullable(it?.value) })
        private set

    fun setParentId(id: Int?) =
        useDb {
            parent = Optional.ofNullable(id)
        }

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