// Weather — Google Weather API via auth-gated key from Firestore.
// If a forecast date isn't available (Google returns up to ~10 days), falls back to climatology.
// Cached in localStorage 6h.

const WEATHER_CACHE_KEY = 'roadtrip-weather-v1';
const WEATHER_TTL_MS    = 6 * 60 * 60 * 1000; // 6 hours

function readWeatherCache() {
  try {
    const raw = localStorage.getItem(WEATHER_CACHE_KEY);
    if (!raw) return null;
    const parsed = JSON.parse(raw);
    if (Date.now() - parsed.fetchedAt > WEATHER_TTL_MS) return null;
    return parsed.byDay;
  } catch { return null; }
}
function writeWeatherCache(byDay) {
  try {
    localStorage.setItem(WEATHER_CACHE_KEY, JSON.stringify({
      fetchedAt: Date.now(),
      byDay,
    }));
  } catch {}
}

function mapGoogleCondition(type, fallbackLabel) {
  const t = (type || "").toUpperCase();
  if (t.includes("THUNDER")) return { label: "Thunderstorm", iconName: "storm" };
  if (t.includes("SNOW") || t.includes("ICE")) return { label: "Snow", iconName: "snow" };
  if (t.includes("RAIN") || t.includes("DRIZZLE") || t.includes("SHOWER")) return { label: "Rain", iconName: "rain" };
  if (t.includes("FOG") || t.includes("MIST") || t.includes("HAZE")) return { label: "Foggy", iconName: "fog" };
  if (t.includes("CLOUD") || t.includes("OVERCAST")) return { label: "Cloudy", iconName: "cloudy" };
  if (t.includes("PARTLY")) return { label: "Partly sunny", iconName: "partly" };
  if (t.includes("CLEAR") || t.includes("SUN")) return { label: "Sunny", iconName: "sun" };
  return { label: fallbackLabel || "Mild", iconName: "partly" };
}

// Try to fetch live forecasts from Google Weather for all trip days. Returns
// { dayN: {high, low, code, source: 'forecast'} } for any day with a result.
async function fetchAllForecasts(apiKey) {
  const BASE = window.WEATHER_BASELINE;
  const DATES = window.WEATHER_DATES;
  const dayNs = Object.keys(BASE).map(Number).sort((a, b) => a - b);

  // Google forecast endpoint returns forward-looking days for a location.
  // Request each day's location; if the trip date is outside the available range,
  // we naturally fall back to climatology.
  const out = {};
  await Promise.all(dayNs.map(async (n) => {
    const b = BASE[n];
    if (!b) return;
    const targetDate = DATES[n];
    const url = `https://weather.googleapis.com/v1/forecast/days:lookup` +
      `?key=${encodeURIComponent(apiKey)}` +
      `&location.latitude=${encodeURIComponent(b.lat)}` +
      `&location.longitude=${encodeURIComponent(b.lng)}` +
      `&days=10&unitsSystem=IMPERIAL`;
    try {
      const r = await fetch(url);
      if (!r.ok) return;
      const j = await r.json();
      const days = Array.isArray(j.forecastDays) ? j.forecastDays : [];
      const match = days.find((d) => {
        const dd = d && d.displayDate;
        if (!dd || !dd.year || !dd.month || !dd.day) return false;
        const month = String(dd.month).padStart(2, "0");
        const day = String(dd.day).padStart(2, "0");
        const iso = `${dd.year}-${month}-${day}`;
        return iso === targetDate;
      });
      if (!match) return;
      const high = match.maxTemperature && typeof match.maxTemperature.degrees === "number"
        ? Math.round(match.maxTemperature.degrees)
        : null;
      const low = match.minTemperature && typeof match.minTemperature.degrees === "number"
        ? Math.round(match.minTemperature.degrees)
        : null;
      if (high == null || low == null) return;
      const conditionType =
        (match.daytimeForecast && match.daytimeForecast.weatherCondition && match.daytimeForecast.weatherCondition.type)
        || (match.nighttimeForecast && match.nighttimeForecast.weatherCondition && match.nighttimeForecast.weatherCondition.type)
        || null;
      const conditionLabel =
        (match.daytimeForecast && match.daytimeForecast.weatherCondition && match.daytimeForecast.weatherCondition.description && match.daytimeForecast.weatherCondition.description.text)
        || (match.nighttimeForecast && match.nighttimeForecast.weatherCondition && match.nighttimeForecast.weatherCondition.description && match.nighttimeForecast.weatherCondition.description.text)
        || null;
      out[n] = {
        high,
        low,
        conditionType,
        conditionLabel,
        source: 'forecast',
      };
    } catch (e) {
      // Network error or out-of-range date → fall through to climatology.
    }
  }));
  return out;
}

// Module-level singleton fetch (so 7 components don't each fire 7 requests)
let _fetchPromise = null;
let _fetchKey = null;
function getForecasts(apiKey) {
  if (!apiKey) return Promise.resolve({});
  if (_fetchPromise && _fetchKey === apiKey) return _fetchPromise;
  if (_fetchPromise && _fetchKey !== apiKey) {
    _fetchPromise = null;
  }
  _fetchKey = apiKey;
  const cached = readWeatherCache();
  if (cached) {
    _fetchPromise = Promise.resolve(cached);
    return _fetchPromise;
  }
  _fetchPromise = fetchAllForecasts(apiKey).then(byDay => {
    writeWeatherCache(byDay);
    return byDay;
  });
  return _fetchPromise;
}

function useRoadtripAuthState() {
  const [auth, setAuth] = React.useState(() => {
    if (window.RoadtripAuth && window.RoadtripAuth.getState) return window.RoadtripAuth.getState();
    return { ready: true, user: null, secrets: null, error: null };
  });
  React.useEffect(() => {
    if (!window.RoadtripAuth || !window.RoadtripAuth.subscribe) return;
    return window.RoadtripAuth.subscribe(setAuth);
  }, []);
  return auth;
}

// Hook — returns the best-available weather for a given day number.
function useWeather(dayN) {
  const baseline = window.WEATHER_BASELINE[dayN];
  const [forecast, setForecast] = React.useState(null);
  const auth = useRoadtripAuthState();

  React.useEffect(() => {
    const weatherKey = auth && auth.secrets
      ? (auth.secrets.weatherApiKey || auth.secrets.gmapsKey || "")
      : "";
    if (!auth.user || !weatherKey) {
      setForecast(null);
      return;
    }
    let cancelled = false;
    getForecasts(weatherKey).then(byDay => {
      if (cancelled) return;
      if (byDay && byDay[dayN]) setForecast(byDay[dayN]);
      else setForecast(null);
    });
    return () => { cancelled = true; };
  }, [dayN, auth.user, auth.secrets && auth.secrets.gmapsKey, auth.secrets && auth.secrets.weatherApiKey]);

  if (forecast) {
    const meta = mapGoogleCondition(forecast.conditionType, forecast.conditionLabel);
    return {
      high: forecast.high,
      low: forecast.low,
      condition: forecast.conditionLabel || meta.label,
      iconName: meta.iconName,
      source: 'forecast',
      location: baseline?.location || '',
    };
  }
  if (baseline) {
    const meta = window.WEATHER_CODE(baseline.code);
    return {
      high: baseline.high,
      low: baseline.low,
      condition: baseline.condition || meta.label,
      iconName: meta.iconName,
      source: 'climatology',
      location: baseline.location,
    };
  }
  return null;
}

// ===== Weather icon — tiny SVG, sized via CSS (font-size on parent + width=1em) =====
function WeatherIcon({ icon, size = 16 }) {
  // Each path is drawn in a 24x24 box. Stroke uses currentColor so it inherits text color.
  const wrap = (children) => (
    <svg
      className="weather-icon"
      width={size}
      height={size}
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      strokeWidth="1.6"
      strokeLinecap="round"
      strokeLinejoin="round"
      aria-hidden="true"
    >{children}</svg>
  );
  switch (icon) {
    case 'sun':
      return wrap(<>
        <circle cx="12" cy="12" r="4" />
        <path d="M12 3v2M12 19v2M3 12h2M19 12h2M5.6 5.6l1.4 1.4M17 17l1.4 1.4M5.6 18.4l1.4-1.4M17 7l1.4-1.4" />
      </>);
    case 'partly':
      return wrap(<>
        <circle cx="9" cy="10" r="3" />
        <path d="M9 4v2M9 14v2M3 10h2M13 10h2M5 6l1.2 1.2M11.8 12.8L13 14M5 14l1.2-1.2M11.8 7.2L13 6" />
        <path d="M9 17h7a3.5 3.5 0 0 0 0-7 5 5 0 0 0-9.5 1.2" />
      </>);
    case 'cloudy':
      return wrap(<path d="M7 18h10a4 4 0 0 0 0-8 5 5 0 0 0-9.5-1A4 4 0 0 0 7 18z" />);
    case 'fog':
      return wrap(<>
        <path d="M7 14h10a4 4 0 0 0 0-8 5 5 0 0 0-9.5-1A4 4 0 0 0 7 14z" />
        <path d="M3 18h18M5 21h14" />
      </>);
    case 'rain':
      return wrap(<>
        <path d="M7 15h10a4 4 0 0 0 0-8 5 5 0 0 0-9.5-1A4 4 0 0 0 7 15z" />
        <path d="M9 18l-1 3M13 18l-1 3M17 18l-1 3" />
      </>);
    case 'snow':
      return wrap(<>
        <path d="M7 15h10a4 4 0 0 0 0-8 5 5 0 0 0-9.5-1A4 4 0 0 0 7 15z" />
        <circle cx="9" cy="20" r=".7" fill="currentColor" stroke="none" />
        <circle cx="13" cy="20" r=".7" fill="currentColor" stroke="none" />
        <circle cx="17" cy="20" r=".7" fill="currentColor" stroke="none" />
      </>);
    case 'storm':
      return wrap(<>
        <path d="M7 14h10a4 4 0 0 0 0-8 5 5 0 0 0-9.5-1A4 4 0 0 0 7 14z" />
        <path d="M11 16l-2 4h3l-1 3 3-4h-3l1-3z" fill="currentColor" stroke="none" />
      </>);
    default:
      return wrap(<circle cx="12" cy="12" r="4" />);
  }
}

// ===== Per-day chip — used inside the day's hero scene =====
function WeatherChip({ dayN }) {
  const w = useWeather(dayN);
  if (!w) return null;
  return (
    <div className="weather-chip" title={`${w.condition} · ${w.source === 'forecast' ? 'live' : 'May average'}`}>
      <WeatherIcon icon={w.iconName} size={15} />
      <span className="wc-temps">{w.high}°<span className="wc-low">/{w.low}°</span></span>
    </div>
  );
}

// ===== Overview strip — small card for each of the 7 days =====
function WeatherStrip() {
  const TRIP = window.TRIP;
  return (
    <div className="weather-strip">
      <div className="ws-head">
        <div className="ws-eyebrow">Weather along the route</div>
        <div className="ws-sub">Late-May normals · upgrades to Google live forecast when available</div>
      </div>
      <div className="ws-grid">
        {TRIP.days.map(d => <WeatherDayCard key={d.n} dayN={d.n} day={d} />)}
      </div>
    </div>
  );
}

function WeatherDayCard({ dayN, day }) {
  const w = useWeather(dayN);
  if (!w) return null;
  // E.g. "May 23" → "23" + "May"
  const dateNum = day.date.replace(/^[A-Za-z]+ [A-Za-z]+ /, '');
  const dow = day.dayOfWeek.slice(0, 3).toUpperCase();
  return (
    <div className="ws-day" title={w.condition}>
      <div className="ws-dow">{dow}</div>
      <div className="ws-date">{dateNum}</div>
      <div className="ws-icon"><WeatherIcon icon={w.iconName} size={22} /></div>
      <div className="ws-high">{w.high}°</div>
      <div className="ws-low">{w.low}°</div>
      <div className="ws-loc">{w.location}</div>
    </div>
  );
}

window.useWeather = useWeather;
window.WeatherChip = WeatherChip;
window.WeatherStrip = WeatherStrip;
