/**
 * Climate Service — derive region context (hardiness zone + climate type)
 * from latitude/longitude or a ZIP/city label.
 *
 * Uses Open-Meteo's free climate API (30-year normals) to estimate the
 * hardiness zone from annual minimum temperature, and a coarse climate type
 * from annual mean temperature + precipitation. NON-FATAL: returns null on
 * any failure — care proceeds without region context.
 */
import type { RegionContext } from '../../types/care';

const CLIMATE_ENDPOINT = 'https://climate-api.open-meteo.com/v1/climate';
const REQUEST_TIMEOUT_MS = 10000;

type ClimateResponse = {
  daily?: {
    temperature_2m_min?: number[];
    temperature_2m_max?: number[];
    precipitation_sum?: number[];
  };
};

/** Map an annual-min-temp (°C) to a USDA hardiness zone. */
function hardinessZoneFromMinTemp(minTempC: number): string {
  // USDA zones keyed by annual extreme minimum temperature (approx).
  if (minTempC <= -45.6) return '1a';
  if (minTempC <= -42.8) return '1b';
  if (minTempC <= -40) return '2a';
  if (minTempC <= -37.2) return '2b';
  if (minTempC <= -34.4) return '3a';
  if (minTempC <= -31.7) return '3b';
  if (minTempC <= -28.9) return '4a';
  if (minTempC <= -26.1) return '4b';
  if (minTempC <= -23.3) return '5a';
  if (minTempC <= -20.6) return '5b';
  if (minTempC <= -17.8) return '6a';
  if (minTempC <= -15) return '6b';
  if (minTempC <= -12.2) return '7a';
  if (minTempC <= -9.4) return '7b';
  if (minTempC <= -6.7) return '8a';
  if (minTempC <= -3.9) return '8b';
  if (minTempC <= -1.1) return '9a';
  if (minTempC <= 1.7) return '9b';
  if (minTempC <= 4.4) return '10a';
  if (minTempC <= 7.2) return '10b';
  if (minTempC <= 10) return '11a';
  return '11b';
}

/** Coarse climate type from annual mean temp + precipitation. */
function climateTypeFromNormals(meanTempC: number, annualPrecipMm: number): string {
  if (meanTempC >= 18 && annualPrecipMm >= 1500) return 'tropical';
  if (meanTempC >= 18 && annualPrecipMm < 1500) return 'tropical-dry';
  if (meanTempC >= 10 && annualPrecipMm >= 600) return 'temperate';
  if (meanTempC >= 10 && annualPrecipMm < 600) return 'temperate-dry';
  if (meanTempC >= 0 && annualPrecipMm >= 400) return 'continental';
  if (meanTempC >= 0 && annualPrecipMm < 400) return 'continental-dry';
  if (annualPrecipMm < 250) return 'arid';
  return 'cold';
}

async function fetchClimate(lat: number, lon: number): Promise<RegionContext | null> {
  const params = new URLSearchParams({
    latitude: String(lat),
    longitude: String(lon),
    daily: 'temperature_2m_min,temperature_2m_max,precipitation_sum',
    // Use a single recent full year — 30 years of daily data is ~300KB (too
    // heavy for mobile). One year gives a usable hardiness/climate estimate
    // at ~10KB. The extreme-min approximation is fine for care guidance.
    start_date: '2020-01-01',
    end_date: '2020-12-31',
    models: 'EC_Earth3P_HR',
  });

  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
  try {
    const response = await fetch(`${CLIMATE_ENDPOINT}?${params.toString()}`, {
      signal: controller.signal,
    });
    if (!response.ok) return null;
    const data = (await response.json()) as ClimateResponse;
    const mins = data.daily?.temperature_2m_min || [];
    const maxs = data.daily?.temperature_2m_max || [];
    const precip = data.daily?.precipitation_sum || [];
    if (mins.length === 0) return null;

    const annualMin = Math.min(...mins);
    const annualMax = Math.max(...maxs);
    const annualPrecip = precip.reduce((a, b) => a + b, 0);
    const meanTemp = (annualMin + annualMax) / 2;

    return {
      hardinessZone: hardinessZoneFromMinTemp(annualMin),
      climateType: climateTypeFromNormals(meanTemp, annualPrecip),
      latitude: lat,
      longitude: lon,
      derivedAt: new Date().toISOString(),
    };
  } catch {
    return null;
  } finally {
    clearTimeout(timeoutId);
  }
}

/**
 * Derive region context from lat/lon. NEVER throws — returns null on failure.
 */
export async function getRegionContext(
  latitude: number,
  longitude: number,
  regionLabel?: string
): Promise<RegionContext | null> {
  const ctx = await fetchClimate(latitude, longitude);
  if (!ctx) return null;
  if (regionLabel) ctx.regionLabel = regionLabel;
  return ctx;
}

/** Render a compact region line for the AI context packet. */
export function renderRegionLine(ctx: RegionContext): string {
  const parts: string[] = [];
  if (ctx.regionLabel) parts.push(ctx.regionLabel);
  if (ctx.hardinessZone) parts.push(`hardiness zone ${ctx.hardinessZone}`);
  if (ctx.climateType) parts.push(`${ctx.climateType} climate`);
  return parts.join(' — ');
}

/**
 * Build a light RegionContext straight from the user's stored GARDEN LOCATION
 * (Bekky, 2026-09-02). The user's garden location label (city / ZIP) is the
 * location signal the AI needs to judge "super local" inputs (regional
 * fertilizer products, local-named treatments, climate-specific feed). No extra
 * network call — the location was already captured. `climateType` is left for the
 * enriched getRegionContext; here we carry the human label so the AI at least
 * knows WHERE the user is. Returns undefined if no label/lat-lon.
 */
export function lightRegionFromGardenLocation(loc: {
  label?: string;
  latitude?: number;
  longitude?: number;
} | null | undefined): RegionContext | undefined {
  if (!loc) return undefined;
  const derivedAt = new Date().toISOString();
  if (loc.label?.trim()) {
    return {
      regionLabel: loc.label.trim(),
      latitude: loc.latitude,
      longitude: loc.longitude,
      derivedAt,
    };
  }
  if (loc.latitude != null && loc.longitude != null) {
    return { latitude: loc.latitude, longitude: loc.longitude, derivedAt };
  }
  return undefined;
}
