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

import llc.arma.fleet.monitor.data.entity.BeaconStatus
import llc.arma.fleet.monitor.data.entity.MachineStateCode
import llc.arma.fleet.monitor.data.repository.MachineRepository
import llc.arma.fleet.monitor.data.repository.MachineStateIntervalRepository
import llc.arma.fleet.monitor.presentation.model.FleetDailyReport
import llc.arma.fleet.monitor.presentation.model.FleetDailyReportHistoryRow
import llc.arma.fleet.monitor.presentation.model.FleetDailyReportMachineRow
import llc.arma.fleet.monitor.presentation.model.FleetDailyReportSummary
import llc.arma.fleet.monitor.presentation.model.GenerateFleetDailyReportPayload
import llc.arma.fleet.monitor.presentation.model.ReportFile
import org.springframework.stereotype.Service
import java.time.LocalDate
import java.time.OffsetDateTime
import java.time.ZoneOffset

@Service
class FleetReportService(
    private val machineRepository: MachineRepository,
    private val intervalRepository: MachineStateIntervalRepository,
) {
    fun getFleetDailyReport(date: LocalDate): FleetDailyReport {
        val machines = machineRepository.findAll()
        val start = date.atStartOfDay().atOffset(ZoneOffset.UTC)
        val end = date.plusDays(1).atStartOfDay().atOffset(ZoneOffset.UTC).minusNanos(1)
        val rows = machines.map { machine ->
            val intervals = intervalRepository.findByMachineIdAndStartedAtLessThanEqualAndEndedAtGreaterThanEqual(machine.id!!, end, start)
            val moving = intervals.filter { it.stateCode == MachineStateCode.MOVING }.sumOf { it.durationSeconds }
            val engineOn = intervals.filter { it.stateCode == MachineStateCode.ENGINE_ON }.sumOf { it.durationSeconds }
            val idle = intervals.filter { it.stateCode == MachineStateCode.IDLE }.sumOf { it.durationSeconds }
            val noData = intervals.filter { it.isNoData || it.stateCode == MachineStateCode.NO_DATA }.sumOf { it.durationSeconds }
            val known = moving + engineOn + idle
            FleetDailyReportMachineRow(
                machine = machine,
                movingSeconds = moving,
                engineOnSeconds = engineOn,
                idleSeconds = idle,
                noDataSeconds = noData,
                stateChangesCount = intervals.size,
                utilizationPercent = if (known == 0) 0 else ((moving.toDouble() / known.toDouble()) * 100).toInt(),
            )
        }
        val history = machines.flatMap { machine ->
            intervalRepository.findByMachineIdAndStartedAtLessThanEqualAndEndedAtGreaterThanEqual(machine.id!!, end, start)
                .map { interval ->
                    FleetDailyReportHistoryRow(
                        machine = machine,
                        startedAt = interval.startedAt,
                        endedAt = interval.endedAt,
                        stateDefinition = interval.stateDefinition,
                        stateCode = interval.stateCode,
                        durationSeconds = interval.durationSeconds,
                    )
                }
        }
        return FleetDailyReport(
            date = date,
            generatedAt = OffsetDateTime.now(),
            summary = FleetDailyReportSummary(
                totalMachines = machines.size,
                boundMachines = machines.count { it.beacon?.status == BeaconStatus.BOUND },
                unboundMachines = machines.count { it.beacon == null },
                machinesWithMovement = rows.count { it.movingSeconds > 0 },
                machinesWithoutData = rows.count { it.noDataSeconds > 0 },
                movingSeconds = rows.sumOf { it.movingSeconds },
                engineOnSeconds = rows.sumOf { it.engineOnSeconds },
                idleSeconds = rows.sumOf { it.idleSeconds },
                noDataSeconds = rows.sumOf { it.noDataSeconds },
                stateChangesCount = rows.sumOf { it.stateChangesCount },
            ),
            machines = rows,
            history = history,
        )
    }

    fun createFleetDailyReportDownloadUrl(payload: GenerateFleetDailyReportPayload): ReportFile =
        ReportFile(
            fileName = "fleet-daily-report-${payload.date}.${payload.format.name.lowercase()}",
            mimeType = when (payload.format.name) {
                "XLSX" -> "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
                else -> "application/pdf"
            },
            downloadUrl = "/api/reports/download/mock/${payload.date}.${payload.format.name.lowercase()}",
            expiresAt = OffsetDateTime.now().plusHours(1),
        )
}
