package llc.arma.fleet.monitor.domain.service

import llc.arma.fleet.monitor.data.entity.MachineCalibrationRuleEntity
import llc.arma.fleet.monitor.data.entity.MachineStateDefinitionEntity
import llc.arma.fleet.monitor.data.repository.MachineCalibrationRuleRepository
import llc.arma.fleet.monitor.data.repository.MachineRepository
import llc.arma.fleet.monitor.data.repository.MachineStateDefinitionRepository
import llc.arma.fleet.monitor.presentation.model.CreateMachineCalibrationRulePayload
import llc.arma.fleet.monitor.presentation.model.CreateMachineStateDefinitionPayload
import llc.arma.fleet.monitor.presentation.model.UpdateMachineCalibrationRulePayload
import llc.arma.fleet.monitor.presentation.model.UpdateMachineStateDefinitionPayload
import org.springframework.stereotype.Service

@Service
class MachineStateService(
    private val stateDefinitionRepository: MachineStateDefinitionRepository,
    private val calibrationRuleRepository: MachineCalibrationRuleRepository,
    private val machineRepository: MachineRepository,
) {
    fun getMachineStateDefinitions(): List<MachineStateDefinitionEntity> = stateDefinitionRepository.findAll()

    fun createMachineStateDefinition(payload: CreateMachineStateDefinitionPayload): MachineStateDefinitionEntity =
        stateDefinitionRepository.save(
            MachineStateDefinitionEntity(
                name = payload.name,
                icon = payload.icon,
                color = payload.color,
                description = payload.description,
                isSystem = false,
            ),
        )

    fun updateMachineStateDefinition(id: Long, payload: UpdateMachineStateDefinitionPayload): MachineStateDefinitionEntity {
        val definition = stateDefinitionRepository.findById(id).orElseThrow { IllegalArgumentException("Состояние не найдено") }
        payload.name?.let { definition.name = it }
        payload.icon?.let { definition.icon = it }
        payload.color?.let { definition.color = it }
        if (payload.description != null) definition.description = payload.description
        return stateDefinitionRepository.save(definition)
    }

    fun deleteMachineStateDefinition(id: Long): Boolean {
        val definition = stateDefinitionRepository.findById(id).orElse(null) ?: return false
        require(!definition.isSystem) { "Системное состояние нельзя удалить" }
        stateDefinitionRepository.delete(definition)
        return true
    }

    fun getMachineCalibrationRules(machineId: Long): List<MachineCalibrationRuleEntity> =
        calibrationRuleRepository.findByMachineId(machineId)

    fun createMachineCalibrationRule(payload: CreateMachineCalibrationRulePayload): MachineCalibrationRuleEntity {
        validateRule(payload.machineId, payload.vibrationMin, payload.vibrationMax, null)
        val machine = machineRepository.findById(payload.machineId).orElseThrow { IllegalArgumentException("Машина не найдена") }
        val definition = stateDefinitionRepository.findById(payload.stateDefinitionId).orElseThrow { IllegalArgumentException("Состояние не найдено") }

        return calibrationRuleRepository.save(
            MachineCalibrationRuleEntity(
                machine = machine,
                stateDefinition = definition,
                vibrationMin = payload.vibrationMin,
                vibrationMax = payload.vibrationMax,
            ),
        )
    }

    fun updateMachineCalibrationRule(id: Long, payload: UpdateMachineCalibrationRulePayload): MachineCalibrationRuleEntity {
        val rule = calibrationRuleRepository.findById(id).orElseThrow { IllegalArgumentException("Правило не найдено") }
        val nextMin = payload.vibrationMin ?: rule.vibrationMin
        val nextMax = payload.vibrationMax ?: rule.vibrationMax
        validateRule(rule.machine.id!!, nextMin, nextMax, id)

        payload.stateDefinitionId?.let {
            rule.stateDefinition = stateDefinitionRepository.findById(it).orElseThrow { IllegalArgumentException("Состояние не найдено") }
        }
        payload.vibrationMin?.let { rule.vibrationMin = it }
        payload.vibrationMax?.let { rule.vibrationMax = it }
        return calibrationRuleRepository.save(rule)
    }

    fun deleteMachineCalibrationRule(id: Long): Boolean {
        if (!calibrationRuleRepository.existsById(id)) return false
        calibrationRuleRepository.deleteById(id)
        return true
    }

    private fun validateRule(machineId: Long, min: Double, max: Double, currentRuleId: Long?) {
        require(min <= max) { "Минимальное значение не может быть больше максимального" }
        val rules = calibrationRuleRepository.findByMachineId(machineId)
        val hasIntersection = rules.any { rule ->
            rule.id != currentRuleId && min <= rule.vibrationMax && max >= rule.vibrationMin
        }
        require(!hasIntersection) { "Диапазон пересекается с существующим правилом" }
    }
}
