package de.kif.common

import de.kif.common.model.ConstraintType
import de.kif.common.model.Schedule
import kotlinx.serialization.Serializable

@Serializable
data class ConstraintError(
    val reason: String = ""
)

@Serializable
data class ConstraintMap(
    val map: List<Pair<Long, List<ConstraintError>>>
)

fun checkConstraints(
    check: List<Schedule>,
    against: List<Schedule>
): ConstraintMap {
    val map = mutableMapOf<Long, List<ConstraintError>>()

    for (schedule in check) {
        if (schedule.id == null) continue
        val errors = mutableListOf<ConstraintError>()

        if (schedule.workGroup.projector && !schedule.room.projector) {
            errors += ConstraintError("Work group requires projector, but room does not have one!")
        }
        if (schedule.workGroup.internet && !schedule.room.internet) {
            errors += ConstraintError("Work group requires internet, but room does not have one!")
        }
        if (schedule.workGroup.whiteboard && !schedule.room.whiteboard) {
            errors += ConstraintError("Work group requires whiteboard, but room does not have one!")
        }
        if (schedule.workGroup.blackboard && !schedule.room.blackboard) {
            errors += ConstraintError("Work group requires blackboard, but room does not have one!")
        }
        if (schedule.workGroup.accessible && !schedule.room.accessible) {
            errors += ConstraintError("Work group requires accessible, but room does not have one!")
        }

        for (constraint in schedule.workGroup.constraints) {
            when (constraint.type) {
                ConstraintType.OnlyOnDay -> {
                    if (constraint.number.toInt() != schedule.day) {
                        errors += ConstraintError("Work group requires day ${constraint.number}, but is on ${schedule.day}!")
                    }
                }
                ConstraintType.OnlyAfterTime -> {
                    if (constraint.number.toInt() > schedule.time) {
                        errors += ConstraintError("Work group requires time after ${constraint.number}, but is on ${schedule.time}!")
                    }
                }
                ConstraintType.NotAtSameTime -> {
                    val start = schedule.getAbsoluteStartTime()
                    val end = schedule.getAbsoluteEndTime()
                    for (s in against) {
                        if (
                            s.workGroup.id == constraint.number &&
                            start <= s.getAbsoluteEndTime() &&
                            s.getAbsoluteStartTime() <= end
                        ) {
                            errors += ConstraintError("Work group requires not same time with ${s.workGroup.name}!")
                        }
                    }
                }
                ConstraintType.OnlyAfterWorkGroup -> {
                    val start = schedule.getAbsoluteStartTime()
                    for (s in against) {
                        if (
                            s.workGroup.id == constraint.number &&
                            s.getAbsoluteEndTime() > start
                        ) {
                            errors += ConstraintError("Work group requires after ${s.workGroup.name}!")
                        }
                    }
                }
            }
        }

        map[schedule.id] = errors
    }

    return ConstraintMap(map.map { it.toPair() })
}
