import { mockMachineDailyUsageStats } from '../mock/mockMachineDailyUsageStats'
import { mockMachineTelemetryIntervals } from '../mock/mockMachineTelemetryIntervals'
import type {
  MachineAnalyticsDateRange,
  MachineAnalyticsPeriod,
  MachineDailyUsageStat,
  MachineTelemetryInterval,
  MachineUsageSummary,
} from '../model/types'

type PeriodRange = {
  start: number
  end: number
}

const hourMs = 60 * 60 * 1000
const dayMs = 24 * hourMs
const fallbackNow = Date.UTC(2026, 6, 9, 12, 0, 0)
const shiftStartHourUtc = 1
const shiftDurationMs = 12 * hourMs

function cloneTelemetryInterval(
  interval: MachineTelemetryInterval,
): MachineTelemetryInterval {
  return { ...interval }
}

function cloneDailyUsageStat(stat: MachineDailyUsageStat): MachineDailyUsageStat {
  return { ...stat }
}

function getDayStart(timestamp: number) {
  const date = new Date(timestamp)

  return Date.UTC(
    date.getUTCFullYear(),
    date.getUTCMonth(),
    date.getUTCDate(),
  )
}

function getIntervalEnd(interval: MachineTelemetryInterval) {
  return new Date(interval.endedAt).getTime()
}

function getLatestMachineTimestamp(machineId: number) {
  const timestamps = mockMachineTelemetryIntervals
    .filter((interval) => interval.machineId === machineId)
    .map(getIntervalEnd)

  return Math.max(0, ...timestamps) || fallbackNow
}

function getDefaultPeriodRange(
  machineId: number,
  period: MachineAnalyticsPeriod,
): PeriodRange {
  const latestTimestamp = getLatestMachineTimestamp(machineId)
  const dayStart = getDayStart(latestTimestamp)

  if (period === 'shift') {
    const shiftStart = dayStart + shiftStartHourUtc * hourMs

    return {
      start: shiftStart,
      end: shiftStart + shiftDurationMs,
    }
  }

  if (period === 'week') {
    return {
      start: dayStart - 6 * dayMs,
      end: dayStart + dayMs,
    }
  }

  if (period === 'month') {
    return {
      start: dayStart - 29 * dayMs,
      end: dayStart + dayMs,
    }
  }

  return {
    start: dayStart,
    end: dayStart + dayMs,
  }
}

function getPeriodRange(
  machineId: number,
  period: MachineAnalyticsPeriod,
  dateRange?: MachineAnalyticsDateRange,
): PeriodRange {
  const defaultRange = getDefaultPeriodRange(machineId, period)
  const start = dateRange?.dateFrom
    ? new Date(dateRange.dateFrom).getTime()
    : defaultRange.start
  const end = dateRange?.dateTo
    ? new Date(dateRange.dateTo).getTime()
    : defaultRange.end

  return {
    start,
    end,
  }
}

function clipIntervalToRange(
  interval: MachineTelemetryInterval,
  range: PeriodRange,
): MachineTelemetryInterval | null {
  const startedAt = new Date(interval.startedAt).getTime()
  const endedAt = new Date(interval.endedAt).getTime()
  const clippedStart = Math.max(startedAt, range.start)
  const clippedEnd = Math.min(endedAt, range.end)

  if (clippedEnd <= clippedStart) return null

  return {
    ...interval,
    startedAt: new Date(clippedStart).toISOString(),
    endedAt: new Date(clippedEnd).toISOString(),
    durationSeconds: Math.floor((clippedEnd - clippedStart) / 1000),
  }
}

function getTelemetryIntervalsInRange(
  machineId: number,
  range: PeriodRange,
) {
  return mockMachineTelemetryIntervals
    .filter((interval) => interval.machineId === machineId)
    .map((interval) => clipIntervalToRange(interval, range))
    .filter((interval): interval is MachineTelemetryInterval => Boolean(interval))
    .sort((first, second) => (
      new Date(first.startedAt).getTime() -
      new Date(second.startedAt).getTime()
    ))
}

function buildSummary(
  machineId: number,
  period: MachineAnalyticsPeriod,
  intervals: MachineTelemetryInterval[],
): MachineUsageSummary {
  const totals = intervals.reduce(
    (result, interval) => {
      if (interval.state === 'MOVING') {
        result.movingSeconds += interval.durationSeconds
      }

      if (interval.state === 'ENGINE_ON') {
        result.engineOnSeconds += interval.durationSeconds
      }

      if (interval.state === 'IDLE') {
        result.idleSeconds += interval.durationSeconds
      }

      if (interval.state === 'NO_DATA') {
        result.noDataSeconds += interval.durationSeconds
      }

      return result
    },
    {
      movingSeconds: 0,
      engineOnSeconds: 0,
      idleSeconds: 0,
      noDataSeconds: 0,
    },
  )
  const totalKnownSeconds =
    totals.movingSeconds + totals.engineOnSeconds + totals.idleSeconds
  const utilizationPercent = totalKnownSeconds > 0
    ? Math.round((totals.movingSeconds / totalKnownSeconds) * 100)
    : 0

  return {
    machineId,
    period,
    ...totals,
    totalKnownSeconds,
    utilizationPercent,
  }
}

function buildDailyStatFromIntervals(
  machineId: number,
  range: PeriodRange,
  intervals: MachineTelemetryInterval[],
): MachineDailyUsageStat {
  const summary = buildSummary(machineId, 'today', intervals)

  return {
    machineId,
    date: new Date(range.start).toISOString().slice(0, 10),
    movingSeconds: summary.movingSeconds,
    engineOnSeconds: summary.engineOnSeconds,
    idleSeconds: summary.idleSeconds,
    noDataSeconds: summary.noDataSeconds,
    utilizationPercent: summary.utilizationPercent,
  }
}

export const machineAnalyticsService = {
  async getMachineTelemetryIntervals(
    machineId: number | string,
    period: MachineAnalyticsPeriod,
    dateRange?: MachineAnalyticsDateRange,
  ): Promise<MachineTelemetryInterval[]> {
    const numericMachineId = Number(machineId)
    const range = getPeriodRange(numericMachineId, period, dateRange)

    return getTelemetryIntervalsInRange(numericMachineId, range)
      .map(cloneTelemetryInterval)
  },

  async getMachineUsageSummary(
    machineId: number | string,
    period: MachineAnalyticsPeriod,
    dateRange?: MachineAnalyticsDateRange,
  ): Promise<MachineUsageSummary> {
    const numericMachineId = Number(machineId)
    const range = getPeriodRange(numericMachineId, period, dateRange)
    const intervals = getTelemetryIntervalsInRange(numericMachineId, range)

    return buildSummary(numericMachineId, period, intervals)
  },

  async getMachineDailyUsageStats(
    machineId: number | string,
    period: MachineAnalyticsPeriod,
    dateRange?: MachineAnalyticsDateRange,
  ): Promise<MachineDailyUsageStat[]> {
    const numericMachineId = Number(machineId)
    const range = getPeriodRange(numericMachineId, period, dateRange)

    if (period === 'shift') {
      const intervals = getTelemetryIntervalsInRange(numericMachineId, range)

      return [buildDailyStatFromIntervals(numericMachineId, range, intervals)]
    }

    return mockMachineDailyUsageStats
      .filter((stat) => {
        if (stat.machineId !== numericMachineId) return false

        const dateStart = new Date(`${stat.date}T00:00:00.000Z`).getTime()

        return dateStart >= range.start && dateStart < range.end
      })
      .sort((first, second) => first.date.localeCompare(second.date))
      .map(cloneDailyUsageStat)
  },
}
