/**
 * Weather-warning persistence.
 *
 * Cached warnings hydrate the notification UI immediately, then a fresh
 * evaluation is reconciled in the background. The fresh set determines which
 * events are active; the persisted set supplies AI advice/tips for an unchanged
 * event so content does not flicker or refetch on every app open.
 */
import type { WeatherWarning } from './weatherWarningService';
import { createJsonFileCache } from './jsonFileCache';
import { asTrimmedString, isRecord, normalizeDateKey, uniqueStrings } from './serviceUtils';

const SEVERITIES = new Set<WeatherWarning['severity']>(['info', 'warning', 'alert']);
const SCOPES = new Set<WeatherWarning['scope']>(['space', 'plant']);
const TRIGGERS = new Set<WeatherWarning['trigger']>(['frost', 'heat', 'storm', 'heavy_rain', 'wind']);

function decodeWarning(value: unknown): WeatherWarning | null {
  if (!isRecord(value)) return null;
  const id = asTrimmedString(value.id, 300);
  const title = asTrimmedString(value.title, 500);
  const message = asTrimmedString(value.message, 1_500);
  const severity = value.severity as WeatherWarning['severity'];
  const scope = value.scope as WeatherWarning['scope'];
  const trigger = value.trigger as WeatherWarning['trigger'];
  if (!id || !title || !message || !SEVERITIES.has(severity) || !SCOPES.has(scope) || !TRIGGERS.has(trigger)) {
    return null;
  }

  const warning: WeatherWarning = { id, title, message, severity, scope, trigger };
  const spaceId = asTrimmedString(value.spaceId, 300);
  const spaceName = asTrimmedString(value.spaceName, 300);
  const plantId = asTrimmedString(value.plantId, 300);
  const plantName = asTrimmedString(value.plantName, 300);
  const advice = asTrimmedString(value.advice, 1_500);
  const tips = asTrimmedString(value.tips, 4_000);
  const date = normalizeDateKey(value.date);
  const plants = Array.isArray(value.plants)
    ? uniqueStrings(value.plants).map((name) => name.slice(0, 300))
    : [];

  if (spaceId) warning.spaceId = spaceId;
  if (spaceName) warning.spaceName = spaceName;
  if (plantId) warning.plantId = plantId;
  if (plantName) warning.plantName = plantName;
  if (plants.length) warning.plants = plants;
  if (advice) warning.advice = advice;
  if (tips) warning.tips = tips;
  if (date) warning.date = date;
  return warning;
}

function decodeWarnings(value: unknown): WeatherWarning[] | null {
  if (!Array.isArray(value)) return null;
  const out: WeatherWarning[] = [];
  const seenIds = new Set<string>();
  for (const item of value) {
    const warning = decodeWarning(item);
    if (!warning || seenIds.has(warning.id)) continue;
    seenIds.add(warning.id);
    out.push(warning);
  }
  return out;
}

const warningsCache = createJsonFileCache<WeatherWarning[]>(
  'weather-warnings-cache.json',
  () => [],
  decodeWarnings,
);

export function loadWeatherWarningsCache(): Promise<WeatherWarning[]> {
  return warningsCache.load();
}

export function saveWeatherWarningsCache(warnings: WeatherWarning[]): Promise<void> {
  return warningsCache.save(decodeWarnings(warnings) ?? []);
}

function sameAdviceContext(previous: WeatherWarning, fresh: WeatherWarning): boolean {
  return previous.severity === fresh.severity
    && previous.trigger === fresh.trigger
    && previous.scope === fresh.scope
    && previous.spaceId === fresh.spaceId
    && previous.spaceName === fresh.spaceName
    && previous.plantId === fresh.plantId
    && previous.plantName === fresh.plantName
    && previous.date === fresh.date
    && previous.title === fresh.title
    && previous.message === fresh.message
    && JSON.stringify(previous.plants || []) === JSON.stringify(fresh.plants || []);
}

/**
 * Reconcile fresh active warnings with persisted AI content. Duplicate or
 * malformed warning IDs are removed, and stale advice is dropped whenever the
 * event's deterministic content or subject changed.
 */
export function reconcileWeatherWarnings(
  fresh: WeatherWarning[],
  persisted: WeatherWarning[],
): WeatherWarning[] {
  const current = decodeWarnings(fresh) ?? [];
  const previous = decodeWarnings(persisted) ?? [];
  const persistedById = new Map(previous.map((warning) => [warning.id, warning] as const));

  return current.map((warning) => {
    const cached = persistedById.get(warning.id);
    if (!cached || !sameAdviceContext(cached, warning)) return warning;
    return {
      ...warning,
      ...(cached.advice ? { advice: cached.advice } : {}),
      ...(cached.tips ? { tips: cached.tips } : {}),
    };
  });
}
