// Hero photo — cloud-first when signed in (uploaded to Supabase Storage, URL
// kept in the homehub_state row), with a localStorage cache used for the very
// first paint and offline fallback. The display URL is held in memory; signed
// URLs from Supabase are short-lived so we DON'T persist them to localStorage.

window.HeroPhoto = (function () {
  const KEY = 'homehub.hero.v1';
  const LEGACY_SIDECAR = '.image-slots.state.json';
  const LEGACY_SLOT = 'hifi-hero-sage';

  let displayUrl = '';
  try { displayUrl = localStorage.getItem(KEY) || ''; } catch (e) {}

  const subs = new Set();
  const notify = () => subs.forEach(fn => { try { fn(displayUrl); } catch (_) {} });

  function get() { return displayUrl; }
  function subscribe(fn) { subs.add(fn); return () => subs.delete(fn); }

  // Internal — used by hifi-cloud.jsx to swap in a freshly-signed URL without
  // triggering another upload. Doesn't touch localStorage.
  function _setDisplay(url) {
    displayUrl = url || '';
    notify();
  }

  // Local-only setter (e.g. for the data-URL fallback). Persists data URLs.
  function _setLocal(url) {
    displayUrl = url || '';
    try {
      if (url && url.startsWith('data:')) localStorage.setItem(KEY, url);
      else if (!url) localStorage.removeItem(KEY);
      // signed URLs: don't overwrite the cache
    } catch (e) {}
    notify();
  }

  // Downscale to a sane size, paint immediately as a data URL, then if Cloud
  // is signed in upload to Supabase and swap to the signed URL.
  function setFromFile(file) {
    return new Promise((resolve, reject) => {
      if (!file || !/^image\//.test(file.type)) { reject(new Error('Not an image')); return; }
      const img = new Image();
      const objUrl = URL.createObjectURL(file);
      img.onload = async () => {
        URL.revokeObjectURL(objUrl);
        const maxW = 1600;
        const scale = Math.min(1, maxW / img.naturalWidth);
        const w = Math.max(1, Math.round(img.naturalWidth * scale));
        const h = Math.max(1, Math.round(img.naturalHeight * scale));
        const cv = document.createElement('canvas');
        cv.width = w; cv.height = h;
        cv.getContext('2d').drawImage(img, 0, 0, w, h);

        let dataUrl = '';
        try { dataUrl = cv.toDataURL('image/jpeg', 0.82); }
        catch (e) { reject(new Error('Encode failed')); return; }

        _setLocal(dataUrl); // fast paint

        if (window.Cloud && window.Cloud.isSignedIn && window.Cloud.isSignedIn()) {
          try {
            const blob = await new Promise(res => cv.toBlob(b => res(b), 'image/jpeg', 0.82));
            if (blob) await window.Cloud.uploadHero(blob);
            // Cloud.uploadHero calls _setDisplay() with the signed URL on success.
            resolve(displayUrl);
          } catch (err) {
            console.error('[Home Hub] Hero cloud upload failed:', err);
            resolve(dataUrl); // local copy still works
          }
        } else {
          resolve(dataUrl);
        }
      };
      img.onerror = () => { URL.revokeObjectURL(objUrl); reject(new Error('Load failed')); };
      img.src = objUrl;
    });
  }

  async function remove() {
    _setLocal('');
    if (window.Cloud && window.Cloud.isSignedIn && window.Cloud.isSignedIn()) {
      try { await window.Cloud.removeHero(); }
      catch (e) { console.error('[Home Hub] Hero cloud remove failed:', e); }
    }
  }

  // Back-compat: older callers do HeroPhoto.set('') to clear. Route that
  // through remove() so the cloud copy goes with it.
  function set(url) {
    if (!url) { remove(); return; }
    _setLocal(url);
  }

  // One-time migration from the old image-slot sidecar (pre-cloud builds).
  (async function migrate() {
    if (displayUrl) return;
    try {
      const r = await fetch(LEGACY_SIDECAR, { cache: 'no-store' });
      if (!r.ok) return;
      const j = await r.json();
      const u = j && j[LEGACY_SLOT] && j[LEGACY_SLOT].u;
      if (u && !displayUrl) _setLocal(u);
    } catch (e) { /* no sidecar on the deployed site — fine */ }
  })();

  return { KEY, get, set, remove, subscribe, setFromFile, _setDisplay };
})();

// Hook: re-renders a component whenever the hero photo changes.
function useHeroPhoto() {
  const [url, setUrl] = React.useState(window.HeroPhoto.get());
  React.useEffect(() => window.HeroPhoto.subscribe(setUrl), []);
  return url;
}

// Full-bleed hero background that the user can drop a photo onto (or click to
// browse when empty). Sits behind the dashboard hero's gradient + controls.
function HeroImage({ t }) {
  const url = useHeroPhoto();
  const inputRef = React.useRef(null);
  const [drag, setDrag] = React.useState(false);
  const [busy, setBusy] = React.useState(false);
  const ingest = (file) => {
    if (!file) return;
    setBusy(true);
    window.HeroPhoto.setFromFile(file).catch(() => {}).then(() => setBusy(false));
  };
  return (
    <div
      onDragOver={(e) => { e.preventDefault(); setDrag(true); }}
      onDragLeave={() => setDrag(false)}
      onDrop={(e) => { e.preventDefault(); setDrag(false); ingest(e.dataTransfer.files && e.dataTransfer.files[0]); }}
      onClick={() => { if (!url && inputRef.current) inputRef.current.click(); }}
      style={{
        position: 'absolute', inset: 0,
        background: url ? `#000 url(${url}) center/cover no-repeat` : (t ? t.inkSoft : '#777'),
        cursor: url ? 'default' : 'pointer',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
      }}>
      {!url && !busy && (
        <div style={{ textAlign: 'center', color: 'rgba(255,255,255,0.92)', pointerEvents: 'none' }}>
          <div style={{ fontSize: 15, fontWeight: 600 }}>Add a photo of your home</div>
          <div style={{ fontSize: 12.5, opacity: 0.85, marginTop: 4 }}>Drop an image here or click to browse</div>
        </div>
      )}
      {busy && <div style={{ color: '#fff', fontSize: 13, pointerEvents: 'none' }}>Saving photo…</div>}
      {drag && <div style={{ position: 'absolute', inset: 10, border: '2px dashed rgba(255,255,255,0.9)', borderRadius: 12, pointerEvents: 'none' }}/>}
      <input ref={inputRef} type="file" accept="image/*" style={{ display: 'none' }}
        onChange={(e) => ingest(e.target.files && e.target.files[0])}/>
    </div>
  );
}

// Compact control for Profile › Appearance to set / replace / remove the photo.
function HeroPicker({ t }) {
  const url = useHeroPhoto();
  const inputRef = React.useRef(null);
  const [busy, setBusy] = React.useState(false);
  const ingest = (file) => {
    if (!file) return;
    setBusy(true);
    window.HeroPhoto.setFromFile(file).catch(() => {}).then(() => setBusy(false));
  };
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 14, padding: '4px 2px' }}>
      <div onClick={() => inputRef.current && inputRef.current.click()} style={{
        width: 104, height: 64, borderRadius: 10, flex: '0 0 auto', cursor: 'pointer', overflow: 'hidden',
        background: url ? `#000 url(${url}) center/cover no-repeat` : t.inputBg,
        border: `1px solid ${t.border}`, display: 'flex', alignItems: 'center', justifyContent: 'center',
        color: t.inkFaint,
      }}>
        {!url && <HIcon kind="drop" size={20}/>}
      </div>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 14, color: t.ink, fontWeight: 600 }}>Home photo</div>
        <div style={{ fontSize: 12.5, color: t.inkSoft, marginTop: 2, lineHeight: 1.4 }}>
          {busy ? 'Saving…' : 'Shown across the top of your dashboard. Synced to your account.'}
        </div>
      </div>
      <div style={{ display: 'flex', gap: 8, flex: '0 0 auto' }}>
        <Btn t={t} onClick={() => inputRef.current && inputRef.current.click()}>
          <HIcon kind="upload" size={14}/> {url ? 'Replace' : 'Upload'}
        </Btn>
        {url && <Btn t={t} onClick={() => window.HeroPhoto.remove()}>Remove</Btn>}
      </div>
      <input ref={inputRef} type="file" accept="image/*" style={{ display: 'none' }}
        onChange={(e) => ingest(e.target.files && e.target.files[0])}/>
    </div>
  );
}

Object.assign(window, { useHeroPhoto, HeroImage, HeroPicker });
