/* ===== Address Entry =====
   Uses the new Places API (REST) directly — no Maps JS SDK needed.
     - POST places:autocomplete  → predictions as the user types
     - POST places/{placeId}     → resolve to formatted address + lat/lng
   The shared sessionToken (a UUID we generate per search session) lets Google
   bill autocomplete requests grouped with the eventual details fetch. */

const PLACES_BASE = 'https://places.googleapis.com/v1';

function uuidv4() {
  if (window.crypto?.randomUUID) return window.crypto.randomUUID();
  // Tiny fallback for older browsers
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
    const r = (Math.random() * 16) | 0;
    return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);
  });
}

async function placesAutocomplete(input, apiKey, sessionToken) {
  const res = await fetch(`${PLACES_BASE}/places:autocomplete`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Goog-Api-Key': apiKey,
    },
    body: JSON.stringify({
      input,
      sessionToken,
      includedPrimaryTypes: ['street_address', 'premise', 'subpremise', 'route'],
    }),
  });
  if (!res.ok) throw new Error(`places autocomplete ${res.status}`);
  const data = await res.json();
  return (data.suggestions || [])
    .map((s) => s.placePrediction)
    .filter(Boolean);
}

async function placesDetails(placeId, apiKey, sessionToken) {
  const res = await fetch(`${PLACES_BASE}/places/${encodeURIComponent(placeId)}?sessionToken=${encodeURIComponent(sessionToken)}`, {
    method: 'GET',
    headers: {
      'X-Goog-Api-Key': apiKey,
      'X-Goog-FieldMask': 'id,formattedAddress,location,addressComponents',
    },
  });
  if (!res.ok) throw new Error(`places details ${res.status}`);
  return res.json();
}

// Geocoding API — works without Places API enabled. Returns first match.
async function geocodeAddress(address, apiKey) {
  const url = `https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURIComponent(address)}&key=${encodeURIComponent(apiKey)}`;
  const res = await fetch(url);
  if (!res.ok) throw new Error(`geocode ${res.status}`);
  const data = await res.json();
  if (data.status !== 'OK' || !data.results?.length) throw new Error(`geocode ${data.status}`);
  return data.results[0]; // { formatted_address, geometry: { location: { lat, lng } }, address_components, place_id }
}

function geocodeResultToSite(r) {
  const lat = r.geometry?.location?.lat;
  const lng = r.geometry?.location?.lng;
  const components = r.address_components || [];
  const findType = (t) => components.find(c => (c.types || []).includes(t));
  const streetNumber = findType('street_number')?.short_name;
  const route = findType('route')?.short_name;
  const locality =
    findType('locality')?.short_name ||
    findType('postal_town')?.short_name ||
    findType('sublocality')?.short_name;
  const region = findType('administrative_area_level_1')?.short_name;
  const postalCode = findType('postal_code')?.short_name;
  const county = findType('administrative_area_level_2')?.short_name;
  const formatted = r.formatted_address || '';
  const streetName = [streetNumber, route].filter(Boolean).join(' ') || (formatted.split(',')[0] || '').trim();
  const shortLabel = locality ? `${(streetName || 'Property').split(' ').slice(-2).join(' ')} · ${locality}` : streetName;
  return { address: formatted, lat, lng, placeId: r.place_id, streetName, locality, region, postalCode, county, shortLabel };
}

// New Places API returns latitude/longitude as numbers + addressComponents with longText/shortText.
function placeToSite(place) {
  const lat = place.location?.latitude;
  const lng = place.location?.longitude;
  const components = place.addressComponents || [];
  const findType = (t) => components.find(c => (c.types || []).includes(t));
  const streetNumber = findType('street_number')?.shortText;
  const route = findType('route')?.shortText;
  const locality =
    findType('locality')?.shortText ||
    findType('postal_town')?.shortText ||
    findType('sublocality')?.shortText;
  const region = findType('administrative_area_level_1')?.shortText;
  const postalCode = findType('postal_code')?.shortText;
  const county = findType('administrative_area_level_2')?.shortText;
  const formatted = place.formattedAddress || '';
  const streetName = [streetNumber, route].filter(Boolean).join(' ') || (formatted.split(',')[0] || '').trim();
  const shortLabel = locality ? `${(streetName || 'Property').split(' ').slice(-2).join(' ')} · ${locality}` : streetName;
  return { address: formatted, lat, lng, placeId: place.id, streetName, locality, region, postalCode, county, shortLabel };
}

const AddressEntry = ({ onContinue, initialSite }) => {
  const apiKey = (window.NOVALE_CONFIG && window.NOVALE_CONFIG.GOOGLE_MAPS_KEY) || '';
  const [site, setSite] = React.useState(initialSite || null);
  const [loading, setLoading] = React.useState(false);
  const [apiReady, setApiReady] = React.useState(false);
  const [apiFailed, setApiFailed] = React.useState(false);
  const inputRef = React.useRef(null);
  const sessionTokenRef = React.useRef(uuidv4());
  const [liveValue, setLiveValue] = React.useState(initialSite?.address || '');
  const [predictions, setPredictions] = React.useState([]);
  const [showPredictions, setShowPredictions] = React.useState(false);

  // Fallback (no key / load failure) — keep prior typed-input behavior.
  const [fallbackValue, setFallbackValue] = React.useState(initialSite?.address || '');
  const [focused, setFocused] = React.useState(true);
  const fallbackSuggestions = [
    '8602 378th Ave SE, Snoqualmie, WA 98065',
    '1142 Olive Way, Seattle, WA 98101',
    '514 Harrison St, Portland, OR 97214',
  ];

  // No SDK to load — we hit Places REST directly. Mark "ready" once we know we have a key.
  React.useEffect(() => {
    if (!apiKey) { setApiFailed(true); return; }
    setApiReady(true);
  }, [apiKey]);

  // Debounced prediction fetch as the user types.
  React.useEffect(() => {
    if (!apiReady || !liveValue || liveValue.length < 3) {
      setPredictions([]);
      return;
    }
    if (site && liveValue === site.address) {
      setPredictions([]);
      return;
    }
    let cancelled = false;
    const t = setTimeout(async () => {
      try {
        const preds = await placesAutocomplete(liveValue, apiKey, sessionTokenRef.current);
        if (cancelled) return;
        setPredictions(preds.slice(0, 5));
        setShowPredictions(true);
      } catch (err) {
        if (cancelled) return;
        console.warn('[novale] autocomplete failed — falling back to Geocoding API', err);
        // Flip the entire UI into Geocoding-fallback mode (typed address + geocode on submit).
        setApiFailed(true);
        setPredictions([]);
      }
    }, 180);
    return () => { cancelled = true; clearTimeout(t); };
  }, [liveValue, apiReady, site, apiKey]);

  const choosePrediction = async (pred) => {
    try {
      const place = await placesDetails(pred.placeId, apiKey, sessionTokenRef.current);
      const next = placeToSite(place);
      setSite(next);
      setLiveValue(next.address);
      setPredictions([]);
      setShowPredictions(false);
      // New session token for the next search (Places billing rule).
      sessionTokenRef.current = uuidv4();
    } catch (err) {
      console.warn('[novale] place details failed', err);
    }
  };

  const handleContinueLive = () => {
    if (!site || loading) return;
    setLoading(true);
    setTimeout(() => onContinue(site), 600);
  };

  const handleContinueFallback = async () => {
    const typed = fallbackValue || fallbackSuggestions[0];
    setLoading(true);
    if (apiKey) {
      try {
        const r = await geocodeAddress(typed, apiKey);
        const next = geocodeResultToSite(r);
        onContinue(next);
        return;
      } catch (err) {
        console.warn('[novale] geocode failed — using offline fallback', err);
      }
    }
    // Last-resort: keep the user moving with a known-good location so the prototype
    // still demos. Real users hitting this path probably have no API key configured.
    onContinue({
      address: typed,
      lat: 47.5301, lng: -121.8254,
      streetName: typed.split(',')[0].trim(),
      locality: 'Snoqualmie', region: 'WA',
      shortLabel: 'Demo property',
      _fallback: true,
    });
  };

  const useFallback = apiFailed || !apiKey;

  return (
    <div className="screen screen-fade-in">
      <div className="screen__main" style={{ maxWidth: 760, textAlign: 'center', paddingTop: 100 }}>
        <div className="eyebrow" style={{ justifyContent: 'center' }}>
          <span className="eyebrow__dot" />Step 01 of 07 · Address
        </div>
        <h1 className="serif" style={{ fontSize: 56, letterSpacing: '-0.022em', marginTop: 16, lineHeight: 1.08 }}>
          Let's start with your property.
        </h1>
        <p className="secondary" style={{ fontSize: 18, marginTop: 16, lineHeight: 1.5 }}>
          We'll pull satellite imagery, your climate zone, soil, and property boundary — so every plant we recommend is right for <em className="serif" style={{ fontStyle: 'italic' }}>this</em> yard.
        </p>

        <div className="address-input" style={{ marginTop: 48 }}>
          {useFallback ? (
            <>
              <div className="address-input__box" data-focused={focused}>
                <Icon name="map-pin" size={20} />
                <input
                  className="address-input__field"
                  value={fallbackValue}
                  onChange={e => setFallbackValue(e.target.value)}
                  onFocus={() => setFocused(true)}
                  placeholder="Start typing your address"
                />
                <button className="btn btn--accent" onClick={handleContinueFallback} disabled={loading || !fallbackValue}>
                  {loading ? <><span className="spinner" /> Analyzing…</> : <>Continue <Icon name="arrow-right" size={16} /></>}
                </button>
              </div>
              {focused && !loading && (
                <div className="address-suggestions">
                  {fallbackSuggestions.map((s, i) => (
                    <button key={i} className="address-suggestion" onClick={() => { setFallbackValue(s); setFocused(false); }}>
                      <Icon name="map-pin" size={14} />
                      <span>{s.split(',')[0]}</span>
                      <span className="muted" style={{ fontSize: 13 }}>{s.split(',').slice(1).join(',')}</span>
                    </button>
                  ))}
                </div>
              )}
            </>
          ) : (
            <>
              <div className="address-input__box address-input__box--places" data-focused={true}>
                <Icon name="map-pin" size={20} />
                <input
                  ref={inputRef}
                  className="address-input__field"
                  value={liveValue}
                  onChange={(e) => {
                    setLiveValue(e.target.value);
                    if (site) setSite(null);
                    setShowPredictions(true);
                  }}
                  onFocus={() => setShowPredictions(true)}
                  onBlur={() => setTimeout(() => setShowPredictions(false), 150)}
                  placeholder={apiReady ? 'Start typing your address' : 'Loading address search…'}
                  disabled={!apiReady}
                  autoComplete="off"
                  spellCheck={false}
                />
                <button
                  className="btn btn--accent"
                  onClick={handleContinueLive}
                  disabled={loading || !site}
                  title={!site ? 'Pick an address from the list' : undefined}
                >
                  {loading ? <><span className="spinner" /> Analyzing…</> : <>Continue <Icon name="arrow-right" size={16} /></>}
                </button>
              </div>
              {showPredictions && predictions.length > 0 && (
                <div className="address-suggestions">
                  {predictions.map((p) => {
                    const main = p.structuredFormat?.mainText?.text || p.text?.text?.split(',')[0] || '';
                    const sub = p.structuredFormat?.secondaryText?.text || (p.text?.text?.split(',').slice(1).join(',').trim() || '');
                    return (
                      <button
                        key={p.placeId}
                        className="address-suggestion"
                        onMouseDown={(e) => e.preventDefault() /* keep input focused */}
                        onClick={() => choosePrediction(p)}
                      >
                        <Icon name="map-pin" size={14} />
                        <span>{main}</span>
                        <span className="muted" style={{ fontSize: 13 }}>{sub}</span>
                      </button>
                    );
                  })}
                  <div className="address-suggestions__attribution">Powered by Google</div>
                </div>
              )}
            </>
          )}

          {site && !useFallback && (
            <div className="address-input__confirmed">
              <Icon name="check" size={14} stroke={2.5} />
              <span>{site.address}</span>
              <span className="muted mono" style={{ fontSize: 11, marginLeft: 'auto' }}>
                {site.lat?.toFixed(4)}, {site.lng?.toFixed(4)}
              </span>
            </div>
          )}
        </div>

        <div className="address-trust">
          <div className="address-trust__item">
            <Icon name="satellite" size={18} />
            <div>
              <div style={{ fontWeight: 500, fontSize: 13 }}>Satellite imagery</div>
              <div className="muted" style={{ fontSize: 12 }}>Mapbox</div>
            </div>
          </div>
          <div className="address-trust__item">
            <Icon name="thermometer-sun" size={18} />
            <div>
              <div style={{ fontWeight: 500, fontSize: 13 }}>USDA zone</div>
              <div className="muted" style={{ fontSize: 12 }}>Hardiness data</div>
            </div>
          </div>
          <div className="address-trust__item">
            <Icon name="square-dashed" size={18} />
            <div>
              <div style={{ fontWeight: 500, fontSize: 13 }}>Parcel lines</div>
              <div className="muted" style={{ fontSize: 12 }}>County assessor</div>
            </div>
          </div>
          <div className="address-trust__item">
            <Icon name="layers" size={18} />
            <div>
              <div style={{ fontWeight: 500, fontSize: 13 }}>Soil & zoning</div>
              <div className="muted" style={{ fontSize: 12 }}>NRCS / City GIS</div>
            </div>
          </div>
        </div>

        <p className="muted" style={{ fontSize: 12, marginTop: 36 }}>
          Don't have an address yet? <a href="#" style={{ color: 'var(--foliage-700)', fontWeight: 500, borderBottom: '1px solid currentColor' }}>Upload a drone photo or site plan</a>
        </p>
      </div>
    </div>
  );
};

Object.assign(window, { AddressEntry });
