package llc.arma.fleet.monitor.config

import llc.arma.fleet.monitor.data.entity.BeaconEntity
import llc.arma.fleet.monitor.data.entity.BeaconStatus
import llc.arma.fleet.monitor.data.entity.DepartmentEntity
import llc.arma.fleet.monitor.data.entity.MachineEntity
import llc.arma.fleet.monitor.data.entity.MachineStateCode
import llc.arma.fleet.monitor.data.entity.MachineStateDefinitionEntity
import llc.arma.fleet.monitor.data.entity.MachineStateIntervalEntity
import llc.arma.fleet.monitor.data.entity.MachineTypeEntity
import llc.arma.fleet.monitor.data.entity.UserEntity
import llc.arma.fleet.monitor.data.entity.UserRole
import llc.arma.fleet.monitor.data.repository.BeaconRepository
import llc.arma.fleet.monitor.data.repository.DepartmentRepository
import llc.arma.fleet.monitor.data.repository.MachineRepository
import llc.arma.fleet.monitor.data.repository.MachineStateDefinitionRepository
import llc.arma.fleet.monitor.data.repository.MachineStateIntervalRepository
import llc.arma.fleet.monitor.data.repository.MachineTypeRepository
import llc.arma.fleet.monitor.data.repository.UserRepository
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.CommandLineRunner
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Component
import java.time.OffsetDateTime

@Component
class DataSeeder(
    @Value("\${app.seed.enabled:true}") private val enabled: Boolean,
    private val userRepository: UserRepository,
    private val machineTypeRepository: MachineTypeRepository,
    private val departmentRepository: DepartmentRepository,
    private val beaconRepository: BeaconRepository,
    private val machineRepository: MachineRepository,
    private val stateDefinitionRepository: MachineStateDefinitionRepository,
    private val stateIntervalRepository: MachineStateIntervalRepository,
    private val passwordEncoder: PasswordEncoder,
) : CommandLineRunner {
    override fun run(vararg args: String) {
        if (!enabled || userRepository.count() > 0) return

        userRepository.saveAll(
            listOf(
                UserEntity(
                    lastName = "Админов",
                    firstName = "Алексей",
                    middleName = "Сергеевич",
                    email = "admin@fleet.local",
                    login = "admin",
                    passwordHash = encodePassword("123456"),
                    role = UserRole.ADMIN,
                ),
                UserEntity(
                    lastName = "Пользователев",
                    firstName = "Иван",
                    middleName = "Петрович",
                    email = "user@fleet.local",
                    login = "user",
                    passwordHash = encodePassword("123456"),
                    role = UserRole.USER,
                ),
            ),
        )

        val roller = machineTypeRepository.save(MachineTypeEntity(name = "Каток", description = "Дорожная техника"))
        val dumpTruck = machineTypeRepository.save(MachineTypeEntity(name = "Самосвал", description = "Грузовая техника"))
        val loader = machineTypeRepository.save(MachineTypeEntity(name = "Погрузчик", description = "Погрузочная техника"))
        val road = departmentRepository.save(DepartmentEntity(name = "Дорожный участок"))
        val mine = departmentRepository.save(DepartmentEntity(name = "Рудничный участок"))

        val beacon1 = beaconRepository.save(BeaconEntity(identifier = "BLE-TRK-0001", macAddress = "E4:5F:01:AA:10:01", status = BeaconStatus.BOUND, lastSeenAt = OffsetDateTime.now().minusMinutes(5)))
        val beacon2 = beaconRepository.save(BeaconEntity(identifier = "BLE-TRK-0002", macAddress = "E4:5F:01:AA:10:02", status = BeaconStatus.BOUND, lastSeenAt = OffsetDateTime.now().minusMinutes(18)))
        beaconRepository.save(BeaconEntity(identifier = "BLE-TRK-0003", macAddress = "E4:5F:01:AA:10:03", status = BeaconStatus.FREE))

        val machine1 = machineRepository.save(
            MachineEntity(
                name = "Каток Hamm 3412",
                licensePlate = "E402PB 154",
                type = roller,
                department = road,
                description = "Уплотнение технологических дорог после отсыпки.",
                beacon = beacon1,
                lastKnownState = MachineStateCode.ENGINE_ON,
                lastDataReceivedAt = beacon1.lastSeenAt,
            ),
        )
        machineRepository.save(
            MachineEntity(
                name = "КамАЗ 6520",
                licensePlate = "A123BC 42",
                type = dumpTruck,
                department = mine,
                beacon = beacon2,
                lastKnownState = MachineStateCode.MOVING,
                lastDataReceivedAt = beacon2.lastSeenAt,
            ),
        )
        machineRepository.save(
            MachineEntity(
                name = "Погрузчик Cat 950",
                licensePlate = "T777TO 42",
                type = loader,
                department = mine,
                lastKnownState = MachineStateCode.NO_DATA,
            ),
        )

        val idle = stateDefinitionRepository.save(MachineStateDefinitionEntity(name = "Стоит", icon = "pause", color = "gray", isSystem = true))
        val engineOn = stateDefinitionRepository.save(MachineStateDefinitionEntity(name = "Заведена", icon = "engine", color = "orange", isSystem = true))
        val moving = stateDefinitionRepository.save(MachineStateDefinitionEntity(name = "В движении", icon = "route", color = "green", isSystem = true))
        val noData = stateDefinitionRepository.save(MachineStateDefinitionEntity(name = "Нет данных", icon = "signal-off", color = "red", isSystem = true))

        val base = OffsetDateTime.now().withHour(8).withMinute(0).withSecond(0).withNano(0)
        stateIntervalRepository.saveAll(
            listOf(
                interval(machine1, engineOn, MachineStateCode.ENGINE_ON, base, 15 * 60),
                interval(machine1, moving, MachineStateCode.MOVING, base.plusMinutes(15), 75 * 60),
                interval(machine1, idle, MachineStateCode.IDLE, base.plusMinutes(90), 40 * 60),
                interval(machine1, moving, MachineStateCode.MOVING, base.plusMinutes(130), 95 * 60),
                interval(machine1, engineOn, MachineStateCode.ENGINE_ON, base.plusMinutes(225), 30 * 60),
                interval(machine1, idle, MachineStateCode.IDLE, base.plusMinutes(255), 45 * 60),
                interval(machine1, moving, MachineStateCode.MOVING, base.plusMinutes(300), 140 * 60),
                interval(machine1, noData, MachineStateCode.NO_DATA, base.plusMinutes(440), 20 * 60, true),
                interval(machine1, idle, MachineStateCode.IDLE, base.plusMinutes(460), 80 * 60),
            ),
        )
    }

    private fun encodePassword(password: String): String {
        return passwordEncoder.encode(password)
            ?: error("Password encoder returned null")
    }

    private fun interval(
        machine: MachineEntity,
        definition: MachineStateDefinitionEntity,
        code: MachineStateCode,
        start: OffsetDateTime,
        durationSeconds: Int,
        noData: Boolean = false,
    ): MachineStateIntervalEntity = MachineStateIntervalEntity(
        machine = machine,
        stateDefinition = definition,
        stateCode = code,
        startedAt = start,
        endedAt = start.plusSeconds(durationSeconds.toLong()),
        durationSeconds = durationSeconds,
        vibrationMin = if (noData) null else 10.0,
        vibrationMax = if (noData) null else 150.0,
        vibrationAvg = if (noData) null else 60.0,
        sampleCount = if (noData) 0 else durationSeconds / 10,
        isNoData = noData,
    )
}
