import { db } from './db.js'
import { getActiveMasterKey } from './auth.js'
import { getLogbookKey } from './logbookKeys.js'
import { decryptJson } from './crypto.js'
import { decryptLogbookTitle } from './logbook.js'
import { getDecryptedTrack, type TrackWaypoint } from './trackUpload.js'
import { compareTravelDaysChronological } from '../utils/logEntryTankLevels.js'
import type { LogEntryPayloadInput } from '../utils/logEntryPayload.js'
import {
  parseEventDistanceNm,
  splitDistanceByPropulsion
} from '../utils/propulsionStats.js'
import { computeFuelPerMotorHour } from '../utils/fuelStats.js'

export type DistanceSource = 'gps' | 'events' | 'none'

export interface TravelDayStats {
  entryId: string
  logbookId: string
  date: string
  dayOfTravel: string
  departure: string
  destination: string
  distanceNm: number
  distanceSource: DistanceSource
  fuelConsumptionL: number
  freshwaterConsumptionL: number
  sailDistanceNm: number
  motorDistanceNm: number
  unknownPropulsionNm: number
  motorHours: number
  fuelPerMotorHourL: number | null
  hasGpsTrack: boolean
}

export interface TrackSegment {
  entryId: string
  dayOfTravel: string
  label: string
  waypoints: TrackWaypoint[]
  colorIndex: number
}

export interface LogbookStatsSummary {
  logbookId: string
  title: string
  travelDays: TravelDayStats[]
  routePorts: string[]
  trackSegments: TrackSegment[]
  totals: StatsTotals
}

export interface AccountStatsSummary {
  logbooks: LogbookStatsSummary[]
  totals: StatsTotals
}

export interface StatsTotals {
  travelDayCount: number
  daysWithGps: number
  totalDistanceNm: number
  sailDistanceNm: number
  motorDistanceNm: number
  unknownPropulsionNm: number
  totalMotorHours: number
  totalFuelL: number
  totalFreshwaterL: number
  avgDistancePerDayNm: number
  avgMotorHoursPerDay: number
  avgFuelPerDayL: number
  avgFreshwaterPerDayL: number
  fuelPerNmL: number | null
  fuelPerMotorHourL: number | null
}

const TRACK_COLORS = [
  '#3b82f6',
  '#10b981',
  '#f59e0b',
  '#8b5cf6',
  '#ec4899',
  '#06b6d4',
  '#ef4444',
  '#84cc16'
]

function resolveDistanceNm(payload: LogEntryPayloadInput): { distanceNm: number; distanceSource: DistanceSource } {
  const gpsDistance = Number(payload.trackDistanceNm) || 0
  if (gpsDistance > 0) {
    return { distanceNm: gpsDistance, distanceSource: 'gps' }
  }

  const eventSum = (payload.events || []).reduce(
    (sum, event) => sum + parseEventDistanceNm(event.distance),
    0
  )
  if (eventSum > 0) {
    return { distanceNm: Number(eventSum.toFixed(2)), distanceSource: 'events' }
  }

  return { distanceNm: 0, distanceSource: 'none' }
}

function buildTotals(days: TravelDayStats[]): StatsTotals {
  const travelDayCount = days.length
  const daysWithGps = days.filter((d) => d.hasGpsTrack).length
  const totalDistanceNm = days.reduce((sum, d) => sum + d.distanceNm, 0)
  const sailDistanceNm = days.reduce((sum, d) => sum + d.sailDistanceNm, 0)
  const motorDistanceNm = days.reduce((sum, d) => sum + d.motorDistanceNm, 0)
  const unknownPropulsionNm = days.reduce((sum, d) => sum + d.unknownPropulsionNm, 0)
  const totalMotorHours = days.reduce((sum, d) => sum + d.motorHours, 0)
  const totalFuelL = days.reduce((sum, d) => sum + d.fuelConsumptionL, 0)
  const totalFreshwaterL = days.reduce((sum, d) => sum + d.freshwaterConsumptionL, 0)

  return {
    travelDayCount,
    daysWithGps,
    totalDistanceNm: Number(totalDistanceNm.toFixed(2)),
    sailDistanceNm: Number(sailDistanceNm.toFixed(2)),
    motorDistanceNm: Number(motorDistanceNm.toFixed(2)),
    unknownPropulsionNm: Number(unknownPropulsionNm.toFixed(2)),
    totalMotorHours: Number(totalMotorHours.toFixed(1)),
    totalFuelL: Number(totalFuelL.toFixed(1)),
    totalFreshwaterL: Number(totalFreshwaterL.toFixed(1)),
    avgDistancePerDayNm:
      travelDayCount > 0 ? Number((totalDistanceNm / travelDayCount).toFixed(2)) : 0,
    avgMotorHoursPerDay:
      travelDayCount > 0 ? Number((totalMotorHours / travelDayCount).toFixed(1)) : 0,
    avgFuelPerDayL:
      travelDayCount > 0 ? Number((totalFuelL / travelDayCount).toFixed(1)) : 0,
    avgFreshwaterPerDayL:
      travelDayCount > 0 ? Number((totalFreshwaterL / travelDayCount).toFixed(1)) : 0,
    fuelPerNmL:
      totalDistanceNm > 0 && totalFuelL > 0
        ? Number((totalFuelL / totalDistanceNm).toFixed(2))
        : null,
    fuelPerMotorHourL: computeFuelPerMotorHour(totalFuelL, totalMotorHours)
  }
}

export function buildRoutePorts(days: TravelDayStats[]): string[] {
  const ports: string[] = []
  for (const day of days) {
    const dep = day.departure.trim()
    const dest = day.destination.trim()
    if (dep && (ports.length === 0 || ports[ports.length - 1] !== dep)) {
      ports.push(dep)
    }
    if (dest && (ports.length === 0 || ports[ports.length - 1] !== dest)) {
      ports.push(dest)
    }
  }
  return ports
}

async function loadTravelDaysForLogbook(
  logbookId: string,
  includeTracks: boolean
): Promise<{ days: TravelDayStats[]; trackSegments: TrackSegment[] }> {
  const masterKey = (await getLogbookKey(logbookId)) || getActiveMasterKey()
  if (!masterKey) {
    throw new Error('Encryption key not found. Please log in.')
  }

  const localEntries = await db.entries.where({ logbookId }).toArray()
  const days: TravelDayStats[] = []
  const trackSegments: TrackSegment[] = []

  for (const entry of localEntries) {
    const decrypted = await decryptJson(entry.encryptedData, entry.iv, entry.tag, masterKey)
    if (!decrypted) continue

    const payload = decrypted as LogEntryPayloadInput
    const { distanceNm, distanceSource } = resolveDistanceNm(payload)
    const propulsion = splitDistanceByPropulsion(distanceNm, payload.events || [])

    let hasGpsTrack = false
    if (includeTracks) {
      const track = await getDecryptedTrack(entry.payloadId)
      if (track && track.waypoints.length >= 2) {
        hasGpsTrack = true
        trackSegments.push({
          entryId: entry.payloadId,
          dayOfTravel: payload.dayOfTravel || '',
          label: payload.dayOfTravel || '?',
          waypoints: track.waypoints,
          colorIndex: trackSegments.length % TRACK_COLORS.length
        })
      }
    } else {
      hasGpsTrack = !!(await db.gpsTracks.get(entry.payloadId))
    }

    const fuelConsumptionL = Number(payload.fuel?.consumption) || 0
    const motorHours = Number(payload.motorHours) || 0

    days.push({
      entryId: entry.payloadId,
      logbookId,
      date: payload.date || '',
      dayOfTravel: payload.dayOfTravel || '',
      departure: payload.departure || '',
      destination: payload.destination || '',
      distanceNm,
      distanceSource,
      fuelConsumptionL,
      freshwaterConsumptionL: Number(payload.freshwater?.consumption) || 0,
      sailDistanceNm: propulsion.sailDistanceNm,
      motorDistanceNm: propulsion.motorDistanceNm,
      unknownPropulsionNm: propulsion.unknownPropulsionNm,
      motorHours,
      fuelPerMotorHourL: computeFuelPerMotorHour(fuelConsumptionL, motorHours),
      hasGpsTrack
    })
  }

  days.sort(compareTravelDaysChronological)
  trackSegments.sort((a, b) => Number(a.dayOfTravel) - Number(b.dayOfTravel))

  return { days, trackSegments }
}

export async function loadLogbookStats(
  logbookId: string,
  title: string,
  includeTracks = true
): Promise<LogbookStatsSummary> {
  const { days, trackSegments } = await loadTravelDaysForLogbook(logbookId, includeTracks)
  return {
    logbookId,
    title,
    travelDays: days,
    routePorts: buildRoutePorts(days),
    trackSegments,
    totals: buildTotals(days)
  }
}

export async function loadAccountStats(includeTracks = false): Promise<AccountStatsSummary> {
  const logbooks = await db.logbooks.toArray()
  const summaries: LogbookStatsSummary[] = []

  for (const lb of logbooks) {
    const title = await decryptLogbookTitle(lb.id, lb.encryptedTitle)
    summaries.push(await loadLogbookStats(lb.id, title, includeTracks))
  }

  summaries.sort((a, b) => a.title.localeCompare(b.title, undefined, { sensitivity: 'base' }))

  const allDays = summaries.flatMap((s) => s.travelDays)
  return {
    logbooks: summaries,
    totals: buildTotals(allDays)
  }
}

export function getTrackColor(index: number): string {
  return TRACK_COLORS[index % TRACK_COLORS.length]
}

export { formatHours, formatLiters, formatNm } from '../utils/numberFormat.js'
