/**
 * Background Weather Task — silent, system-scheduled weather refresh.
 *
 * WHY (Bekky, 2026-09-03): the app should be "already oriented" when the user
 * opens it — the day's forecast fetched in the background as early as the OS
 * allows, so the first render reads a FRESH once-per-day cache instead of
 * yesterday's (or a blank). NO notification is shown — this is a silent
 * background fetch, never a 5am ping. The OS controls exactly when it runs
 * (inexact, battery-aware); we just ask for the soonest reasonable interval.
 *
 * The weather service already caches once per calendar day (weatherService.ts,
 * readCache/writeCache). This task simply: read the persisted garden location →
 * call getWeather() → it writes the fresh cache. When the app opens,
 * readCachedWeather() hydrates instantly from that cache. Non-fatal: any
 * failure just leaves the existing cache in place.
 *
 * EXTENDED (2026-09-03, orchestration Phase 3): the task ALSO pre-computes the
 * weather-alert advice cache headlessly. It loads the garden state, evaluates
 * weather warnings, and for any ALERT without cached advice, fetches the short
 * AI action line (through the serial AI queue) and persists it. So when the
 * app opens, alert advice is already settled — no AI call fires on open, no
 * "changing data before your eyes." All best-effort; any failure leaves the
 * existing cache intact.
 */
import * as TaskManager from 'expo-task-manager';
import * as BackgroundTask from 'expo-background-task';

import { loadAppSettings, loadGardenInfrastructure, loadSavedPlantProfiles } from '../gardenStorage';
import { getWeather } from '../weather';
import {
  evaluateWeatherWarnings,
  enrichAlertAdvice,
  alertSignature,
} from '../care/weatherWarningService';
import { loadAlertAdviceCache, saveAlertAdviceCache } from '../care/alertAdviceCache';
import { enqueueAiCall } from '../orchestration/aiQueue';
import { markRefreshCompleted } from '../orchestration/refreshStamp';

export const BACKGROUND_WEATHER_TASK = 'pixiesprout-background-weather';

/** How often the OS should consider running the task (minutes). The system
 *  treats this as a MINIMUM delay and may run it later to save battery. */
const MINIMUM_INTERVAL_MINUTES = 60;

TaskManager.defineTask(BACKGROUND_WEATHER_TASK, async () => {
  try {
    const settings = await loadAppSettings();
    const loc = settings.privacy?.gardenLocation;
    if (!loc || loc.latitude == null || loc.longitude == null) {
      // No garden location set — nothing to fetch. Not an error.
      return BackgroundTask.BackgroundTaskResult.Success;
    }
    // getWeather() reads the once-per-day cache and only hits Open-Meteo if
    // today's forecast isn't cached yet — so this is cheap on days the app
    // was already opened. Writes the fresh cache for the next open.
    const weather = await getWeather(loc.latitude, loc.longitude, 'rough');
    if (!weather) {
      // Weather unavailable — nothing further to do (warnings need weather).
      return BackgroundTask.BackgroundTaskResult.Success;
    }

    // Pre-compute the weather-alert advice cache headlessly (Phase 3).
    const adviceOk = await refreshAlertAdvice(weather);

    // Mark the refresh stamp ONLY if the advice pass genuinely completed
    // (expert review 2026-09-03). If it failed, do NOT stamp fresh — the
    // on-open fallback must run so alerts still get AI advice. Cohort care is
    // NOT part of this background pass — it stays hash-gated on open (the
    // complex grouping logic isn't headless-callable; keeping it on open is
    // more reliable). The stamp only covers weather + alert advice.
    if (adviceOk) await markRefreshCompleted();

    return BackgroundTask.BackgroundTaskResult.Success;
  } catch (e) {
    if (typeof __DEV__ !== 'undefined' && __DEV__) {
      console.log('[Pixie-diag] background weather task failed', e);
    }
    return BackgroundTask.BackgroundTaskResult.Failed;
  }
});

/** Evaluate weather warnings and pre-fill the alert-advice cache for any
 *  ALERT that doesn't yet have advice. Best-effort; never throws.
 *  Returns true if the pass genuinely completed (no alerts to enrich, or all
 *  alerts got advice), false if it failed partway — so the caller only marks
 *  the refresh stamp on real success (expert review 2026-09-03). */
async function refreshAlertAdvice(weather: import('../../types/care').WeatherSnapshot): Promise<boolean> {
  try {
    const [infra, plants] = await Promise.all([
      loadGardenInfrastructure(),
      loadSavedPlantProfiles(),
    ]);
    const warnings = evaluateWeatherWarnings({
      plants,
      rooms: infra.rooms,
      weather,
    });
    const alerts = warnings.filter(w => w.severity === 'alert');
    if (!alerts.length) return true; // nothing to enrich = success

    const cache = await loadAlertAdviceCache();
    let changed = false;
    for (const w of alerts) {
      const sig = alertSignature(w);
      if (cache[sig]) continue; // already have advice for this signature
      const trigger = w.trigger === 'wind' ? 'heavy_rain' : w.trigger;
      const advice = await enqueueAiCall(() => enrichAlertAdvice({
        plantName: w.plantName || w.spaceName || 'your plants',
        spaceName: w.spaceName,
        trigger,
        date: w.date,
        detail: w.message,
        subjectSig: sig,
      }));
      if (advice) {
        cache[sig] = advice;
        changed = true;
      }
    }
    if (changed) await saveAlertAdviceCache(cache);
    // Return true ONLY if at least one alert got advice. If every advice call
    // returned null (silent AI failure), return false so the caller does NOT
    // mark the stamp fresh — the on-open fallback must run again next open
    // (expert review 2026-09-03, null-path).
    return changed;
  } catch {
    // Best-effort — a failed advice refresh never fails the task, but the
    // caller must NOT mark the stamp fresh (the on-open fallback should run).
    return false;
  }
}

/** Register the background task. Call once at app startup (idempotent). */
export async function registerBackgroundWeatherTask(): Promise<void> {
  try {
    const status = await BackgroundTask.getStatusAsync();
    if (status !== BackgroundTask.BackgroundTaskStatus.Available) {
      // Background tasks unavailable (e.g. restricted) — skip silently.
      return;
    }
    await BackgroundTask.registerTaskAsync(BACKGROUND_WEATHER_TASK, {
      minimumInterval: MINIMUM_INTERVAL_MINUTES,
    });
  } catch (e) {
    if (typeof __DEV__ !== 'undefined' && __DEV__) {
      console.log('[Pixie-diag] background weather task registration failed', e);
    }
  }
}
