import { type LonLatPoint } from '../../utils/projection'
import { type GameLaunchEnvelope } from '../../utils/gameLaunch'
import { type GameSessionState } from './gameSessionState'

export interface RecoveryTelemetrySnapshot {
  distanceMeters: number
  currentSpeedKmh: number | null
  averageSpeedKmh: number | null
  heartRateBpm: number | null
  caloriesKcal: number | null
  lastGpsPoint: LonLatPoint | null
  lastGpsAt: number | null
  lastGpsAccuracyMeters: number | null
}

export interface RecoveryViewportSnapshot {
  zoom: number
  centerTileX: number
  centerTileY: number
  rotationDeg: number
  gpsLockEnabled: boolean
  hasGpsCenteredOnce: boolean
}

export interface RecoveryRuntimeSnapshot {
  gameState: GameSessionState
  telemetry: RecoveryTelemetrySnapshot
  viewport: RecoveryViewportSnapshot
  currentGpsPoint: LonLatPoint | null
  currentGpsAccuracyMeters: number | null
  currentGpsInsideMap: boolean
  bonusScore: number
  quizCorrectCount: number
  quizWrongCount: number
  quizTimeoutCount: number
}

export interface SessionRecoverySnapshot {
  schemaVersion: 1
  savedAt: number
  launchEnvelope: GameLaunchEnvelope
  configAppId: string
  configVersion: string
  runtime: RecoveryRuntimeSnapshot
}

const SESSION_RECOVERY_STORAGE_KEY = 'cmr.sessionRecovery.v1'

function cloneLonLatPoint(point: LonLatPoint | null): LonLatPoint | null {
  if (!point) {
    return null
  }
  return {
    lon: point.lon,
    lat: point.lat,
  }
}

function cloneGameSessionState(state: GameSessionState): GameSessionState {
  return {
    status: state.status,
    endReason: state.endReason,
    startedAt: state.startedAt,
    endedAt: state.endedAt,
    completedControlIds: state.completedControlIds.slice(),
    skippedControlIds: state.skippedControlIds.slice(),
    currentTargetControlId: state.currentTargetControlId,
    inRangeControlId: state.inRangeControlId,
    score: state.score,
    guidanceState: state.guidanceState,
    modeState: state.modeState
      ? JSON.parse(JSON.stringify(state.modeState)) as Record<string, unknown>
      : null,
  }
}

export function cloneSessionRecoverySnapshot(snapshot: SessionRecoverySnapshot): SessionRecoverySnapshot {
  return {
    schemaVersion: 1,
    savedAt: snapshot.savedAt,
    launchEnvelope: JSON.parse(JSON.stringify(snapshot.launchEnvelope)) as GameLaunchEnvelope,
    configAppId: snapshot.configAppId,
    configVersion: snapshot.configVersion,
    runtime: {
      gameState: cloneGameSessionState(snapshot.runtime.gameState),
      telemetry: {
        distanceMeters: snapshot.runtime.telemetry.distanceMeters,
        currentSpeedKmh: snapshot.runtime.telemetry.currentSpeedKmh,
        averageSpeedKmh: snapshot.runtime.telemetry.averageSpeedKmh,
        heartRateBpm: snapshot.runtime.telemetry.heartRateBpm,
        caloriesKcal: snapshot.runtime.telemetry.caloriesKcal,
        lastGpsPoint: cloneLonLatPoint(snapshot.runtime.telemetry.lastGpsPoint),
        lastGpsAt: snapshot.runtime.telemetry.lastGpsAt,
        lastGpsAccuracyMeters: snapshot.runtime.telemetry.lastGpsAccuracyMeters,
      },
      viewport: {
        zoom: snapshot.runtime.viewport.zoom,
        centerTileX: snapshot.runtime.viewport.centerTileX,
        centerTileY: snapshot.runtime.viewport.centerTileY,
        rotationDeg: snapshot.runtime.viewport.rotationDeg,
        gpsLockEnabled: snapshot.runtime.viewport.gpsLockEnabled,
        hasGpsCenteredOnce: snapshot.runtime.viewport.hasGpsCenteredOnce,
      },
      currentGpsPoint: cloneLonLatPoint(snapshot.runtime.currentGpsPoint),
      currentGpsAccuracyMeters: snapshot.runtime.currentGpsAccuracyMeters,
      currentGpsInsideMap: snapshot.runtime.currentGpsInsideMap,
      bonusScore: snapshot.runtime.bonusScore,
      quizCorrectCount: snapshot.runtime.quizCorrectCount,
      quizWrongCount: snapshot.runtime.quizWrongCount,
      quizTimeoutCount: snapshot.runtime.quizTimeoutCount,
    },
  }
}

function normalizeSessionRecoverySnapshot(raw: unknown): SessionRecoverySnapshot | null {
  if (!raw || typeof raw !== 'object') {
    return null
  }

  const candidate = raw as SessionRecoverySnapshot
  if (candidate.schemaVersion !== 1 || !candidate.runtime || !candidate.runtime.gameState) {
    return null
  }

  return cloneSessionRecoverySnapshot(candidate)
}

export function loadSessionRecoverySnapshot(): SessionRecoverySnapshot | null {
  try {
    return normalizeSessionRecoverySnapshot(wx.getStorageSync(SESSION_RECOVERY_STORAGE_KEY))
  } catch {
    return null
  }
}

export function saveSessionRecoverySnapshot(snapshot: SessionRecoverySnapshot): void {
  try {
    wx.setStorageSync(SESSION_RECOVERY_STORAGE_KEY, cloneSessionRecoverySnapshot(snapshot))
  } catch {}
}

export function clearSessionRecoverySnapshot(): void {
  try {
    wx.removeStorageSync(SESSION_RECOVERY_STORAGE_KEY)
  } catch {}
}
