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

import llc.arma.fleet.monitor.data.entity.MachineStateCode
import llc.arma.fleet.monitor.data.entity.MachineStateIntervalEntity
import llc.arma.fleet.monitor.data.repository.MachineRepository
import llc.arma.fleet.monitor.data.repository.MachineStateIntervalRepository
import llc.arma.fleet.monitor.presentation.model.MachineDailyUsageStat
import llc.arma.fleet.monitor.presentation.model.MachineStateHistoryPage
import llc.arma.fleet.monitor.presentation.model.MachineUsageSummary
import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Sort
import org.springframework.stereotype.Service
import java.time.LocalDate
import java.time.OffsetDateTime

@Service
class MachineAnalyticsService(
    private val machineRepository: MachineRepository,
    private val stateIntervalRepository: MachineStateIntervalRepository,
) {
    fun getMachineStateHistory(
        machineId: Long,
        startDate: OffsetDateTime,
        endDate: OffsetDateTime,
        page: Int?,
        pageSize: Int?,
    ): MachineStateHistoryPage {
        val pageable = PageRequest.of(
            (page ?: 0).coerceAtLeast(0),
            (pageSize ?: 50).coerceIn(1, 200),
            Sort.by(Sort.Direction.DESC, "startedAt"),
        )
        val result = stateIntervalRepository.findByMachineIdAndStartedAtBetween(machineId, startDate, endDate, pageable)
        return MachineStateHistoryPage(result.totalPages, result.totalElements, result.content)
    }

    fun getMachineUsageSummary(machineId: Long, startDate: OffsetDateTime, endDate: OffsetDateTime): MachineUsageSummary {
        val machine = machineRepository.findById(machineId).orElseThrow { IllegalArgumentException("Машина не найдена") }
        val intervals = stateIntervalRepository.findByMachineIdAndStartedAtLessThanEqualAndEndedAtGreaterThanEqual(machineId, endDate, startDate)
        val moving = intervals.durationFor(MachineStateCode.MOVING)
        val engineOn = intervals.durationFor(MachineStateCode.ENGINE_ON)
        val idle = intervals.durationFor(MachineStateCode.IDLE)
        val noData = intervals.filter { it.isNoData || it.stateCode == MachineStateCode.NO_DATA }.sumOf { it.durationSeconds }
        val known = moving + engineOn + idle
        val utilization = if (known == 0) 0 else ((moving.toDouble() / known.toDouble()) * 100).toInt()

        return MachineUsageSummary(
            machine = machine,
            periodStart = startDate,
            periodEnd = endDate,
            movingSeconds = moving,
            engineOnSeconds = engineOn,
            idleSeconds = idle,
            noDataSeconds = noData,
            stateChangesCount = intervals.size,
            utilizationPercent = utilization,
        )
    }

    fun getMachineDailyUsageStats(machineId: Long, startDate: OffsetDateTime, endDate: OffsetDateTime): List<MachineDailyUsageStat> {
        val machine = machineRepository.findById(machineId).orElseThrow { IllegalArgumentException("Машина не найдена") }
        val result = mutableListOf<MachineDailyUsageStat>()
        var date = startDate.toLocalDate()
        val lastDate = endDate.toLocalDate()
        while (!date.isAfter(lastDate)) {
            val dayStart = date.atStartOfDay().atOffset(startDate.offset)
            val dayEnd = date.plusDays(1).atStartOfDay().atOffset(startDate.offset).minusNanos(1)
            val summary = getMachineUsageSummary(machineId, dayStart, dayEnd)
            result.add(
                MachineDailyUsageStat(
                    machine = machine,
                    date = date.toString(),
                    movingSeconds = summary.movingSeconds,
                    engineOnSeconds = summary.engineOnSeconds,
                    idleSeconds = summary.idleSeconds,
                    noDataSeconds = summary.noDataSeconds,
                    utilizationPercent = summary.utilizationPercent,
                ),
            )
            date = date.plusDays(1)
        }
        return result
    }

    fun getUnrecognizedTelemetryIntervals(machineId: Long, startDate: OffsetDateTime, endDate: OffsetDateTime): List<MachineStateIntervalEntity> =
        stateIntervalRepository
            .findByMachineIdAndStartedAtLessThanEqualAndEndedAtGreaterThanEqual(machineId, endDate, startDate)
            .filter { it.stateDefinition == null && !it.isNoData }

    private fun List<MachineStateIntervalEntity>.durationFor(state: MachineStateCode): Int =
        filter { it.stateCode == state }.sumOf { it.durationSeconds }
}
