import { getPoiRepository } from '@/data/poi'
import {
  clonePlanResponse,
  cloneTravelPreferences,
  normalizePlanResponse,
  normalizeTravelPreferences,
  type PlanningRequest,
  type PlanResponse,
  type TravelPreferences,
} from '@/domain/travel'

export const PLAN_STORAGE_KEY = 'guangming:last-plan'
export const PREFERENCES_STORAGE_KEY = 'guangming:last-preferences'
export const PLANNING_REQUEST_STORAGE_KEY = 'guangming:last-planning-request'

const STORAGE_SCHEMA_VERSION = 1

interface StorageEnvelope<T> {
  schemaVersion: typeof STORAGE_SCHEMA_VERSION
  datasetVersion: string
  savedAt: string
  payload: T
}

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === 'object' && value !== null && !Array.isArray(value)
}

function createEnvelope<T>(payload: T): StorageEnvelope<T> {
  return {
    schemaVersion: STORAGE_SCHEMA_VERSION,
    datasetVersion: getPoiRepository().getDatasetMeta().datasetVersion,
    savedAt: new Date().toISOString(),
    payload,
  }
}

function readEnvelope(value: unknown): StorageEnvelope<unknown> | null {
  if (!isRecord(value) || value.schemaVersion !== STORAGE_SCHEMA_VERSION)
    return null
  if (typeof value.datasetVersion !== 'string' || !value.datasetVersion.trim())
    return null
  if (typeof value.savedAt !== 'string' || !Number.isFinite(Date.parse(value.savedAt)))
    return null
  if (!('payload' in value))
    return null
  return value as unknown as StorageEnvelope<unknown>
}

function readStorage(key: string): unknown {
  try {
    return uni.getStorageSync(key)
  }
  catch {
    return null
  }
}

function writeStorage(key: string, value: unknown): void {
  try {
    uni.setStorageSync(key, value)
  }
  catch (error) {
    const reason = error instanceof Error && error.message ? `：${error.message}` : ''
    throw new Error(`无法保存本机行程数据${reason}`)
  }
}

export function savePlan(plan: PlanResponse): void {
  const repository = getPoiRepository()
  const normalized = normalizePlanResponse(plan, repository)
  writeStorage(PLAN_STORAGE_KEY, createEnvelope(clonePlanResponse(normalized, repository)))
}

export function loadPlan(): PlanResponse | null {
  const repository = getPoiRepository()
  const envelope = readEnvelope(readStorage(PLAN_STORAGE_KEY))
  if (!envelope || envelope.datasetVersion !== repository.getDatasetMeta().datasetVersion)
    return null
  try {
    const plan = normalizePlanResponse(envelope.payload, repository)
    return plan.source === 'local_poc' ? plan : null
  }
  catch {
    return null
  }
}

export function savePreferences(preferences: TravelPreferences): void {
  const normalized = normalizeTravelPreferences(preferences)
  writeStorage(PREFERENCES_STORAGE_KEY, createEnvelope(cloneTravelPreferences(normalized)))
}

export function loadPreferences(): TravelPreferences | null {
  const envelope = readEnvelope(readStorage(PREFERENCES_STORAGE_KEY))
  if (!envelope)
    return null
  try {
    return normalizeTravelPreferences(envelope.payload)
  }
  catch {
    return null
  }
}

export function savePlanningRequest(request: PlanningRequest): void {
  const repository = getPoiRepository()
  const sanitized: PlanningRequest = {
    mode: request.mode,
    preferences: normalizeTravelPreferences(request.preferences),
    durationMinutes: request.durationMinutes,
    selectedPoiIds: [...request.selectedPoiIds],
  }
  if (sanitized.mode !== 'quick' && sanitized.mode !== 'custom')
    throw new Error('无法保存规划条件：规划模式无效')
  if (!Number.isInteger(sanitized.durationMinutes)
    || sanitized.durationMinutes < 120
    || sanitized.durationMinutes > 600
    || sanitized.durationMinutes % 30 !== 0) {
    throw new Error('无法保存规划条件：规划时长无效')
  }
  if (sanitized.mode === 'quick' && sanitized.selectedPoiIds.length > 0)
    throw new Error('无法保存规划条件：快速规划不能预选点位')
  if (sanitized.mode === 'custom'
    && (sanitized.selectedPoiIds.length < 2 || sanitized.selectedPoiIds.length > 8)) {
    throw new Error('无法保存规划条件：自选点位数量无效')
  }
  if (new Set(sanitized.selectedPoiIds).size !== sanitized.selectedPoiIds.length
    || sanitized.selectedPoiIds.some(poiId => !repository.getPoiById(poiId))) {
    throw new Error('无法保存规划条件：自选点位无效')
  }
  writeStorage(PLANNING_REQUEST_STORAGE_KEY, createEnvelope(sanitized))
}

export function loadPlanningRequest(): PlanningRequest | null {
  const repository = getPoiRepository()
  const envelope = readEnvelope(readStorage(PLANNING_REQUEST_STORAGE_KEY))
  if (!envelope || envelope.datasetVersion !== repository.getDatasetMeta().datasetVersion || !isRecord(envelope.payload))
    return null
  try {
    const payload = envelope.payload
    const request: PlanningRequest = {
      mode: payload.mode as PlanningRequest['mode'],
      preferences: normalizeTravelPreferences(payload.preferences),
      durationMinutes: Number(payload.durationMinutes),
      selectedPoiIds: Array.isArray(payload.selectedPoiIds) ? payload.selectedPoiIds.map(String) : [],
    }
    if (request.mode !== 'quick' && request.mode !== 'custom')
      return null
    if (!Number.isInteger(request.durationMinutes)
      || request.durationMinutes < 120
      || request.durationMinutes > 600
      || request.durationMinutes % 30 !== 0) {
      return null
    }
    if (request.mode === 'quick' && request.selectedPoiIds.length > 0)
      return null
    if (request.mode === 'custom' && (request.selectedPoiIds.length < 2 || request.selectedPoiIds.length > 8))
      return null
    if (new Set(request.selectedPoiIds).size !== request.selectedPoiIds.length
      || request.selectedPoiIds.some(poiId => !repository.getPoiById(poiId))) {
      return null
    }
    return request
  }
  catch {
    return null
  }
}
