/**
 * steps.jsx — Step components for the virtual estimate flow.
 *
 *   StepIndicator — labelled, numbered progress pills
 *   AddressStep   — Step 1: address input
 *   PhotosStep    — Step 2: optional photo upload
 *   LoadingStep   — Step 3: loading state with branded copy
 *   ResultsStep   — Step 4: tiered service estimates with promise banner
 *
 * Brand language follows README.md:
 *   • Title Case marketing headlines
 *   • "You" + "we" voice
 *   • Specific numbers over vague claims
 *   • Eyebrow labels for section intros
 *   • Raspberry pill CTAs only
 */

const STEPS = [
  { id: 1, label: 'Address' },
  { id: 2, label: 'Photos' },
  { id: 3, label: 'Estimate' },
  { id: 4, label: 'Results' },
  { id: 5, label: 'Schedule' },
];

// ── Step indicator ────────────────────────────────────────────────────────────

function StepIndicator({ current, onJump }) {
  return (
    <ol className="step-indicator" aria-label="Estimate progress">
      {STEPS.map(s => {
        const state =
          s.id === current ? ' is-active'
          : s.id < current  ? ' is-complete'
          : '';
        const reachable = s.id < current && typeof onJump === 'function';
        const handleClick = reachable ? () => onJump(s.id) : undefined;
        return (
          <li
            key={s.id}
            className={'step-pill' + state + (reachable ? ' is-clickable' : '')}
            role={reachable ? 'button' : undefined}
            tabIndex={reachable ? 0 : undefined}
            aria-label={reachable ? `Go back to ${s.label}` : undefined}
            onClick={handleClick}
            onKeyDown={reachable ? (e) => {
              if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onJump(s.id); }
            } : undefined}
          >
            <span className="step-pill__bubble" aria-hidden="true">
              {s.id < current ? <CheckIcon size={14} color="#fff" /> : s.id}
            </span>
            <span className="step-pill__label">{s.label}</span>
          </li>
        );
      })}
    </ol>
  );
}

// ── Persistent address banner (steps 2-4) ────────────────────────────────────

function AddressEditBanner({ address, onEdit, disabled }) {
  return (
    <div className="address-banner" role="region" aria-label="Current estimate address">
      <div className="address-banner__main">
        <MapPinIcon size={18} />
        <div className="address-banner__text">
          <span className="address-banner__label">Estimating for</span>
          <span className="address-banner__value" title={address}>{address}</span>
        </div>
      </div>
      <button
        type="button"
        className="address-banner__edit"
        onClick={onEdit}
        disabled={disabled}
      >
        <EditIcon size={13} /> Change
      </button>
    </div>
  );
}

// ── Step 1: Address ───────────────────────────────────────────────────────────

function AddressStep({ address, onChange, onSubmit, onSuggestionSelect, onError, loading }) {
  const { useState, useEffect, useRef } = React;
  const [suggestions, setSuggestions]   = useState([]);
  const [showSuggest, setShowSuggest]   = useState(false);
  const [highlight, setHighlight]       = useState(-1);
  const [detectingLoc, setDetectingLoc] = useState(false);
  const wrapperRef    = useRef(null);
  const debounceRef   = useRef(null);
  const requestIdRef  = useRef(0);
  // After picking a suggestion the parent updates `address` synchronously;
  // we don't want that change to immediately trigger another fetch.
  const skipNextFetchRef = useRef(false);

  useEffect(() => {
    if (skipNextFetchRef.current) {
      skipNextFetchRef.current = false;
      return;
    }
    if (debounceRef.current) clearTimeout(debounceRef.current);

    const query = address.trim();
    if (query.length < 3) {
      setSuggestions([]);
      setShowSuggest(false);
      return;
    }

    debounceRef.current = setTimeout(async () => {
      const reqId = ++requestIdRef.current;
      try {
        const data = await window.MollyAPI.suggestAddresses(query);
        if (reqId !== requestIdRef.current) return; // stale response
        const list = data.suggestions || [];
        setSuggestions(list);
        setShowSuggest(list.length > 0);
        setHighlight(-1);
      } catch {
        if (reqId !== requestIdRef.current) return;
        setSuggestions([]);
        setShowSuggest(false);
      }
    }, 220);

    return () => {
      if (debounceRef.current) clearTimeout(debounceRef.current);
    };
  }, [address]);

  useEffect(() => {
    function onDocMouseDown(e) {
      if (wrapperRef.current && !wrapperRef.current.contains(e.target)) {
        setShowSuggest(false);
      }
    }
    document.addEventListener('mousedown', onDocMouseDown);
    return () => document.removeEventListener('mousedown', onDocMouseDown);
  }, []);

  function pickSuggestion(s) {
    skipNextFetchRef.current = true;
    // Invalidate any in-flight suggestion request so it can't repopulate.
    requestIdRef.current += 1;
    if (debounceRef.current) clearTimeout(debounceRef.current);
    setSuggestions([]);
    setShowSuggest(false);
    setHighlight(-1);
    onSuggestionSelect(s.description);
  }

  async function handleUseMyLocation() {
    if (!('geolocation' in navigator)) {
      onError && onError('Your browser doesn\'t support location detection. Please type your address.');
      return;
    }

    setDetectingLoc(true);
    onError && onError(null);

    try {
      const position = await new Promise((resolve, reject) => {
        navigator.geolocation.getCurrentPosition(resolve, reject, {
          enableHighAccuracy: true,
          timeout: 10000,
          maximumAge: 60000,
        });
      });

      const { latitude, longitude } = position.coords;
      const data = await window.MollyAPI.reverseGeocode(latitude, longitude);
      if (!data.address) throw new Error('No address');

      // Reuse the suggestion-pick path: prefill input + run property lookup + advance.
      skipNextFetchRef.current = true;
      requestIdRef.current += 1;
      setSuggestions([]);
      setShowSuggest(false);
      onSuggestionSelect(data.address);
    } catch (err) {
      let message = 'We couldn\'t find an address at your current location. Please type your address.';
      if (err && err.code === 1) {
        message = 'Location access was blocked. Allow it in your browser or type your address below.';
      } else if (err && err.code === 2) {
        message = 'Your location is unavailable right now. Please type your address.';
      } else if (err && err.code === 3) {
        message = 'Location request timed out. Try again, or type your address.';
      }
      onError && onError(message);
    } finally {
      setDetectingLoc(false);
    }
  }

  function handleKeyDown(e) {
    if (!showSuggest || suggestions.length === 0) return;
    if (e.key === 'ArrowDown') {
      e.preventDefault();
      setHighlight(h => Math.min(h + 1, suggestions.length - 1));
    } else if (e.key === 'ArrowUp') {
      e.preventDefault();
      setHighlight(h => Math.max(h - 1, 0));
    } else if (e.key === 'Enter' && highlight >= 0) {
      e.preventDefault();
      pickSuggestion(suggestions[highlight]);
    } else if (e.key === 'Escape') {
      setShowSuggest(false);
    }
  }

  return (
    <div className="card">
      <div className="card__head">
        <p className="section-eyebrow">Step 1 · Property Details</p>
        <h2 className="card__title">What's the property address?</h2>
        <p className="card__sub">
          We'll automatically pull square footage, bedrooms, and bathrooms so
          you don't have to measure anything. Your address is only used for
          this estimate.
        </p>
      </div>

      <form onSubmit={onSubmit}>
        <div className="field address-field" ref={wrapperRef}>
          <div className="field__label-row">
            <label htmlFor="address">Street Address</label>
            <button
              type="button"
              className="location-btn"
              onClick={handleUseMyLocation}
              disabled={detectingLoc || loading}
              aria-label="Use my current location"
            >
              <CrosshairIcon size={13} />
              {detectingLoc ? 'Finding…' : 'Use my location'}
            </button>
          </div>
          <input
            id="address"
            type="text"
            placeholder="123 Main St, Springfield, IL 62701"
            value={address}
            onChange={e => onChange(e.target.value)}
            onFocus={() => { if (suggestions.length > 0) setShowSuggest(true); }}
            onKeyDown={handleKeyDown}
            required
            autoFocus
            autoComplete="off"
            role="combobox"
            aria-autocomplete="list"
            aria-expanded={showSuggest}
            aria-controls="address-suggestions"
          />
          {showSuggest && suggestions.length > 0 && (
            <ul id="address-suggestions" className="address-suggest" role="listbox">
              {suggestions.map((s, i) => (
                <li
                  key={s.placeId || i}
                  className={'address-suggest__item' + (i === highlight ? ' is-highlight' : '')}
                  role="option"
                  aria-selected={i === highlight}
                  onMouseDown={e => { e.preventDefault(); pickSuggestion(s); }}
                  onMouseEnter={() => setHighlight(i)}
                >
                  <MapPinIcon size={14} />
                  <span className="address-suggest__text">
                    <span className="address-suggest__primary">
                      {s.mainText || s.description}
                    </span>
                    {s.secondaryText && (
                      <span className="address-suggest__secondary">
                        {s.secondaryText}
                      </span>
                    )}
                  </span>
                </li>
              ))}
            </ul>
          )}
        </div>

        <div className="btn-row">
          <button
            className="btn-primary btn-primary--large"
            type="submit"
            disabled={loading || !address.trim()}
          >
            {loading ? 'Looking Up…' : 'Continue'}
            {!loading && <ArrowRightIcon size={16} color="#fff" />}
          </button>
          <p className="card__sub card__sub--small" style={{ margin: 0 }}>
            <InfoIcon size={12} /> Takes about 90 seconds, no signup required.
          </p>
        </div>
      </form>
    </div>
  );
}

// ── Step 2: Photos ────────────────────────────────────────────────────────────

// Editable property fields shown atop the Photos step. The Sq Ft / Beds /
// Baths come pre-filled from the address-lookup API but a customer can
// override any of them here — when they continue to the estimate, the
// updated values flow straight into getEstimate via the App's `property`
// state.
function PropertyEditor({ property, onChange }) {
  const { useState } = React;
  // Local string for sq-ft so a user can blank the field while typing.
  // Committed back to the parent on blur (or when a valid number appears).
  const [sqftStr, setSqftStr] = useState(
    property ? String(property.squareFootage) : ''
  );
  React.useEffect(() => {
    if (property) setSqftStr(String(property.squareFootage));
  }, [property && property.squareFootage]);

  if (!property) return null;

  const update = (key, value) => onChange({ ...property, [key]: value });
  const clamp = (n, lo, hi) => Math.max(lo, Math.min(hi, n));

  function commitSqft(next) {
    const n = Number(next);
    if (Number.isFinite(n) && n > 0) {
      update('squareFootage', clamp(Math.round(n), 300, 10000));
    } else {
      setSqftStr(String(property.squareFootage));
    }
  }

  return (
    <div className="prop-editor">
      <p className="prop-editor__hint">
        <InfoIcon size={12} />
        <span>
          We pre-filled this from public records for your address —
          tap to fix anything that's off.
        </span>
      </p>

      <div className="prop-editor__grid">
        {/* Square footage — numeric input + step buttons (±100) */}
        <div className="prop-editor__cell">
          <span className="prop-editor__label">
            <HomeIcon size={13} /> Square Feet
          </span>
          <div className="prop-editor__stepper">
            <button
              type="button"
              className="prop-editor__btn"
              aria-label="Decrease square feet"
              onClick={() => update('squareFootage', clamp(property.squareFootage - 100, 300, 10000))}
            >−</button>
            <input
              type="number"
              min="300"
              max="10000"
              step="50"
              value={sqftStr}
              onChange={(e) => setSqftStr(e.target.value)}
              onBlur={(e) => commitSqft(e.target.value)}
              onKeyDown={(e) => { if (e.key === 'Enter') e.currentTarget.blur(); }}
              className="prop-editor__input"
              aria-label="Square feet"
            />
            <button
              type="button"
              className="prop-editor__btn"
              aria-label="Increase square feet"
              onClick={() => update('squareFootage', clamp(property.squareFootage + 100, 300, 10000))}
            >+</button>
          </div>
        </div>

        {/* Bedrooms — stepper only (no need to type 8 vs 9) */}
        <div className="prop-editor__cell">
          <span className="prop-editor__label">
            <BedIcon size={13} /> Bedrooms
          </span>
          <div className="prop-editor__stepper">
            <button
              type="button"
              className="prop-editor__btn"
              aria-label="Decrease bedrooms"
              onClick={() => update('bedrooms', clamp(property.bedrooms - 1, 1, 12))}
            >−</button>
            <span className="prop-editor__num" aria-live="polite">{property.bedrooms}</span>
            <button
              type="button"
              className="prop-editor__btn"
              aria-label="Increase bedrooms"
              onClick={() => update('bedrooms', clamp(property.bedrooms + 1, 1, 12))}
            >+</button>
          </div>
        </div>

        {/* Bathrooms — stepper, half-bath increments */}
        <div className="prop-editor__cell">
          <span className="prop-editor__label">
            <BathIcon size={13} /> Bathrooms
          </span>
          <div className="prop-editor__stepper">
            <button
              type="button"
              className="prop-editor__btn"
              aria-label="Decrease bathrooms"
              onClick={() => update('bathrooms', clamp(property.bathrooms - 0.5, 0.5, 10))}
            >−</button>
            <span className="prop-editor__num" aria-live="polite">
              {Number.isInteger(property.bathrooms) ? property.bathrooms : property.bathrooms.toFixed(1)}
            </span>
            <button
              type="button"
              className="prop-editor__btn"
              aria-label="Increase bathrooms"
              onClick={() => update('bathrooms', clamp(property.bathrooms + 0.5, 0.5, 10))}
            >+</button>
          </div>
        </div>
      </div>
    </div>
  );
}

function PhotosStep({ property, onPropertyChange, onSubmit, onSkip, loading }) {
  const { useState, useRef } = React;
  const [files, setFiles]       = useState([]);
  const [dragOver, setDragOver] = useState(false);
  const inputRef                = useRef(null);
  const cameraRef               = useRef(null);

  // HEIC: iOS Safari sometimes leaves f.type empty for HEIC/HEIF rather than
  // emitting image/heic, so we fall back to the filename extension.
  const HEIC_EXT = /\.(heic|heif)$/i;
  function isImageOrHeic(f) {
    return (f.type && f.type.startsWith('image/')) || HEIC_EXT.test(f.name || '');
  }

  function handleFiles(incoming) {
    const imageFiles = Array.from(incoming).filter(isImageOrHeic);
    setFiles(prev => [...prev, ...imageFiles]);
  }

  function handleDrop(e) {
    e.preventDefault();
    setDragOver(false);
    handleFiles(e.dataTransfer.files);
  }

  function handleSubmit() {
    onSubmit(files);
  }

  return (
    <div className="card">
      <div className="card__head">
        <p className="section-eyebrow">Step 2 · Tailor Your Quote</p>
        <h2 className="card__title">
          Share a Few Photos <span style={{
            fontWeight: 'var(--mly-fw-medium)',
            color: 'var(--mly-ink-3)',
            fontSize: '0.7em',
            letterSpacing: 0,
            textTransform: 'none',
          }}>(optional)</span>
        </h2>
        <p className="card__sub">
          Snap a quick photo of your kitchen, bathroom, or living areas and
          we'll tune the estimate to match the actual condition of your home.
          No photos? You can skip ahead — we'll use a standard baseline.
        </p>

        <PropertyEditor property={property} onChange={onPropertyChange} />
      </div>

      <div
        className={'upload-zone' + (dragOver ? ' is-drag' : '')}
        onClick={() => inputRef.current?.click()}
        onDragOver={e => { e.preventDefault(); setDragOver(true); }}
        onDragLeave={() => setDragOver(false)}
        onDrop={handleDrop}
        role="button"
        tabIndex={0}
        onKeyDown={e => { if (e.key === 'Enter' || e.key === ' ') inputRef.current?.click(); }}
      >
        <UploadIcon />
        <p className="upload-zone__title">
          {files.length > 0
            ? `${files.length} photo${files.length > 1 ? 's' : ''} ready to analyze`
            : 'Drag photos here or tap to choose'}
        </p>
        <p className="upload-zone__sub">JPG, PNG, or HEIC · up to 10 photos · analyzed privately</p>

        {files.length > 0 && (
          <span className="upload-zone__count">
            <CheckIcon size={14} /> Photos selected
          </span>
        )}

        {/* The drop zone is one big click target — these clarify the two
            ways to add photos on a phone: tap the zone to pick from the
            camera roll, or use the "Take a photo" button to open the
            camera directly. Accept includes HEIC for iPhone photos. */}
        <input
          ref={inputRef}
          type="file"
          accept="image/*,image/heic,image/heif,.heic,.heif"
          multiple
          style={{ display: 'none' }}
          onChange={e => { handleFiles(e.target.files); e.target.value = ''; }}
          onClick={e => e.stopPropagation()}
        />

        <div className="upload-zone__actions" onClick={e => e.stopPropagation()}>
          <button
            type="button"
            className="upload-zone__action"
            onClick={() => cameraRef.current?.click()}
          >
            <CameraIcon size={15} /> Take a Photo
          </button>
          <button
            type="button"
            className="upload-zone__action upload-zone__action--alt"
            onClick={() => inputRef.current?.click()}
          >
            <UploadIcon size={15} color="var(--mly-navy)" /> Choose Files
          </button>

          {/* Hidden input for camera capture — `capture="environment"`
              makes mobile browsers open the rear camera directly. Desktops
              that don't honor `capture` fall back to a normal file dialog. */}
          <input
            ref={cameraRef}
            type="file"
            accept="image/*"
            capture="environment"
            style={{ display: 'none' }}
            onChange={e => { handleFiles(e.target.files); e.target.value = ''; }}
          />
        </div>
      </div>

      <p className="card__hint">
        <InfoIcon /> Photos are reviewed by AI to estimate cleanliness — they
        are not stored or shared. A Molly Maid pro will confirm everything
        in person before your first clean.
      </p>

      <div className="btn-row">
        <button
          className="btn-primary btn-primary--large"
          onClick={handleSubmit}
          disabled={loading || files.length === 0}
        >
          {loading ? 'Analyzing…' : 'Analyze & Get Estimate'}
          {!loading && <ArrowRightIcon size={16} color="#fff" />}
        </button>
        <button className="btn-tertiary" onClick={onSkip} disabled={loading}>
          Skip and use a standard baseline
        </button>
      </div>
    </div>
  );
}

// ── Step 3: Loading ───────────────────────────────────────────────────────────

function LoadingStep() {
  return (
    <div className="card loading-card">
      <div className="loading-spinner" aria-hidden="true" />
      <h2 className="loading-card__title">Putting Together Your Estimate</h2>
      <p className="loading-card__sub">
        Pricing your home against our 44-point cleaning checklist…
      </p>
    </div>
  );
}

// Step 4 (Results) is defined in results.jsx so it can house multiple
// tier-layout variants (cards / compare / stack) without bloating this file.

function ResultsStep_LEGACY_UNUSED({ property, analysis, services, onReset }) {
  const lowestPrice = services && services.length
    ? Math.min(...services.map(s => s.price))
    : null;

  return (
    <div>
      {/* Heading row */}
      <div style={{ marginBottom: 20 }}>
        <p className="section-eyebrow">Your Virtual Estimate</p>
        <h2 className="section-title">
          Here's What a Clean Looks Like for Your Home
        </h2>
        <p className="section-lede">
          Estimated pricing based on your home's size, layout
          {analysis && !analysis._stub ? ', and a quick AI review of the photos you shared' : ''}.
          A Molly Maid pro confirms everything in person before your first clean.
        </p>
      </div>

      {/* Summary chips + (optional) cleanliness score */}
      <div className="results-head">
        <div>
          {property && (
            <div className="prop-summary">
              <span className="prop-chip">
                <MapPinIcon /> {property.address || 'Your Home'}
              </span>
              <span className="prop-chip">
                <HomeIcon /> {property.squareFootage.toLocaleString()} sq ft
              </span>
              <span className="prop-chip">
                <BedIcon /> {property.bedrooms} {property.bedrooms === 1 ? 'bed' : 'beds'}
              </span>
              <span className="prop-chip">
                <BathIcon /> {property.bathrooms} {property.bathrooms === 1 ? 'bath' : 'baths'}
              </span>
            </div>
          )}

          {analysis && analysis.findings && analysis.findings.length > 0 && (
            <>
              <h3 style={{
                margin: '20px 0 6px',
                color: 'var(--mly-navy)',
                fontSize: 14,
                fontWeight: 700,
                letterSpacing: 'var(--mly-ls-eyebrow)',
                textTransform: 'uppercase',
              }}>
                What Our Review Noted
              </h3>
              <ul className="findings-list">
                {analysis.findings.map((f, i) => <li key={i}>{f}</li>)}
              </ul>
            </>
          )}
        </div>

        {analysis && !analysis._stub && typeof analysis.score === 'number' && (
          <div className="score-block">
            <p className="score-block__label">Cleanliness Score</p>
            <p className="score-block__value">
              {analysis.score}<small> / 100</small>
            </p>
            <p className="score-block__sub">
              Based on the {analysis.imageCount || 'photos'} you shared.
            </p>
          </div>
        )}
      </div>

      {/* Service tiers */}
      <div className="service-list">
        {services.map(svc => {
          const meta = SERVICE_DETAILS[svc.name] || {};
          return (
            <article
              key={svc.name}
              className={'service-card' + (meta.popular ? ' service-card--popular' : '')}
            >
              {meta.popular && <span className="service-card__tag">Most Popular</span>}
              <p className="service-card__eyebrow">{meta.eyebrow || svc.name}</p>
              <h3 className="service-card__name">{svc.name}</h3>
              <p className="service-card__price">
                {window.MollyUtils.formatPrice(svc.price)}
                <small> / clean</small>
              </p>
              <p className="service-card__desc">{svc.description}</p>

              {meta.bullets && (
                <ul className="service-card__bullets">
                  {meta.bullets.map((b, i) => (
                    <li key={i}>
                      <CheckIcon size={16} /> <span>{b}</span>
                    </li>
                  ))}
                </ul>
              )}

              <div className="service-card__cta">
                <button className={meta.popular ? 'btn-primary' : 'btn-secondary'}>
                  Choose {svc.name.replace(' / Move Out', '')}
                </button>
              </div>
            </article>
          );
        })}
      </div>

      {/* Neighborly Done Right Promise */}
      <div className="promise-banner">
        <div className="promise-banner__icon">
          <ShieldIcon size={28} />
        </div>
        <div>
          <p className="promise-banner__title">Backed by the Neighborly Done Right Promise®</p>
          <p className="promise-banner__sub">
            If it's not done right, we'll make it right. Every clean is backed by
            our re-clean guarantee — no fine print, no extra charges.
          </p>
        </div>
        <button className="btn-primary" onClick={onReset}>
          Start Over
        </button>
      </div>

      {/* Trust strip */}
      <div className="trust-strip">
        <div className="trust-item">
          <span className="trust-item__icon"><StarIcon size={18} /></span>
          <div>
            <p className="trust-item__title">1.5M+ customers served</p>
            <p className="trust-item__sub">Cleaning homes across the U.S. since 1984.</p>
          </div>
        </div>
        <div className="trust-item">
          <span className="trust-item__icon"><SparkleIcon size={20} /></span>
          <div>
            <p className="trust-item__title">44-point checklist</p>
            <p className="trust-item__sub">Every room, every visit — nothing skipped.</p>
          </div>
        </div>
        <div className="trust-item">
          <span className="trust-item__icon"><ClockIcon size={18} /></span>
          <div>
            <p className="trust-item__title">Bonded, insured, background-checked</p>
            <p className="trust-item__sub">Same trusted team every visit, when possible.</p>
          </div>
        </div>
      </div>

      <p style={{
        textAlign: 'center',
        color: 'var(--mly-ink-3)',
        fontSize: 'var(--mly-fs-caption)',
        margin: '36px auto 0',
        maxWidth: '52ch',
        lineHeight: 1.55,
      }}>
        Prices shown are virtual estimates and are not a final quote. Final
        pricing is confirmed in writing by your local Molly Maid before your
        first clean. Service availability varies by location.
      </p>
    </div>
  );
}
