// WeddingOS · sync bridge + license gate.
// Sits at the outer layer of the app. Three jobs:
//   1. Gate: block app behind license activation (server validates against Worker)
//   2. Pull: on activation, fetch server state if any, hydrate localStorage, reload
//   3. Push: debounced PUT of full state to Worker as the user edits
//
// All API calls go to window.WOS_SYNC_ENDPOINT. If empty, gate falls open
// (dev mode) so the design canvas continues to work standalone.

const __LICENSE_KEY    = 'weddingos.license';      // raw code: WO-XXXX-XXXX-XXXX
const __WEDDING_ID_KEY = 'weddingos.weddingId';    // server-issued opaque id
const __PUSH_DEBOUNCE_MS = 2500;

// ─── API client ───────────────────────────────────────────────
async function wosApi(path, init = {}) {
  const base = window.WOS_SYNC_ENDPOINT;
  if (!base) throw new Error('sync_disabled');
  const ctrl = new AbortController();
  const t = setTimeout(() => ctrl.abort(), 8000);
  try {
    const res = await fetch(`${base}${path}`, { ...init, signal: ctrl.signal });
    const data = await res.json().catch(() => ({}));
    return { ok: res.ok, status: res.status, data };
  } finally { clearTimeout(t); }
}

async function wosRedeem(code) {
  return wosApi('/license/redeem', {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify({ code }),
  });
}
async function wosCheck(code) {
  return wosApi('/license/check', {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify({ code }),
  });
}
async function wosPullWedding(code, weddingId) {
  return wosApi(`/wedding/${weddingId}`, { headers: { 'x-license': code } });
}
async function wosPushWedding(code, weddingId, data) {
  return wosApi(`/wedding/${weddingId}`, {
    method: 'PUT',
    headers: { 'content-type': 'application/json', 'x-license': code },
    body: JSON.stringify({ data }),
  });
}
async function wosCreatePair(code, weddingId) {
  return wosApi('/pair', {
    method: 'POST',
    headers: { 'content-type': 'application/json', 'x-license': code },
    body: JSON.stringify({ weddingId }),
  });
}
async function wosConsumePair(pair) {
  return wosApi(`/pair/${pair}`);
}

// Sync enabled = endpoint configured
function wosEnabled() { return !!window.WOS_SYNC_ENDPOINT; }

Object.assign(window, {
  wosRedeem, wosCheck, wosPullWedding, wosPushWedding,
  wosCreatePair, wosConsumePair, wosEnabled,
});

// ─── License gate ─────────────────────────────────────────────
// Wraps the entire app. Blocks until the user has a valid, redeemed license.
// After successful activation, optionally pulls existing wedding state from
// the server and seeds it into localStorage before mounting children.
function LicenseGate({ children }) {
  const [licensed, setLicensed] = React.useState(() => {
    return !!localStorage.getItem(__LICENSE_KEY);
  });

  if (!wosEnabled()) return children; // dev fallback
  if (licensed) return children;
  return <ActivateGate onActivated={() => setLicensed(true)} />;
}

function ActivateGate({ onActivated }) {
  const segRefs = [React.useRef(null), React.useRef(null), React.useRef(null)];
  const [segs, setSegs] = React.useState(['', '', '']);
  const [error, setError] = React.useState(null);
  const [busy, setBusy] = React.useState(false);

  const onSegChange = (i, raw) => {
    const v = raw.toUpperCase().replace(/[^0-9A-Z]/g, '').slice(0, 4);
    const next = [...segs]; next[i] = v; setSegs(next);
    if (v.length === 4 && i < 2) segRefs[i + 1].current && segRefs[i + 1].current.focus();
  };

  const onSegKey = (i, e) => {
    if (e.key === 'Backspace' && !segs[i] && i > 0) {
      segRefs[i - 1].current && segRefs[i - 1].current.focus();
    }
    if (e.key === 'Enter') tryActivate();
  };

  const code = `WO-${segs[0]}-${segs[1]}-${segs[2]}`;
  const complete = segs.every(s => s.length === 4);

  async function tryActivate() {
    setError(null);
    if (!complete) { setError('Enter all 12 characters from your Etsy receipt.'); return; }
    setBusy(true);
    try {
      const res = await wosRedeem(code);
      if (!res.ok) {
        setError(res.data?.error === 'invalid code'
          ? "That code isn't recognized. Check your Etsy receipt."
          : `Activation failed: ${res.data?.error || 'unknown error'}`);
        setBusy(false);
        return;
      }
      // Stash license + weddingId.
      localStorage.setItem(__LICENSE_KEY, code);
      localStorage.setItem(__WEDDING_ID_KEY, res.data.weddingId);

      if (res.data.firstUse) {
        // First activation on any device · wipe the demo wedding (Sloane & Theo)
        // and write an empty state so onboarding triggers cleanly.
        try {
          const empty = (typeof window.makeEmptyState === 'function')
            ? window.makeEmptyState()
            : null;
          if (empty) {
            localStorage.setItem('weddingos.v1', JSON.stringify({ ...empty, updatedAt: Date.now() }));
          } else {
            // Fallback: nuke any persisted state so loadInitial returns defaults · then we'll
            // route to onboarding via the production-entry's hasName check below.
            localStorage.removeItem('weddingos.v1');
          }
        } catch {}
      } else {
        // Returning device · pull existing server state into localStorage.
        const pulled = await wosPullWedding(code, res.data.weddingId);
        if (pulled.ok && pulled.data?.data) {
          try {
            localStorage.setItem('weddingos.v1', JSON.stringify(pulled.data.data));
          } catch {}
        }
      }
      onActivated();
      // Force a clean mount so seeded state applies cleanly.
      setTimeout(() => window.location.reload(), 50);
    } catch (err) {
      setError(`Network error · ${err.message}`);
      setBusy(false);
    }
  }

  // Visual matches the design canvas's ActivateScreen aesthetic.
  return (
    <div style={{
      minHeight: '100vh', minHeight: '100dvh',
      background: (typeof W !== 'undefined' && W.bg) || '#faf6ec',
      display: 'flex', flexDirection: 'column', alignItems: 'center',
      padding: '56px 22px 36px',
    }}>
      <div style={{
        width: 56, height: 56, borderRadius: 999, marginTop: 8,
        background: (typeof W !== 'undefined' && W.cardSoft) || '#fbf6ea',
        border: '0.5px solid rgba(0,0,0,0.08)',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
      }}>
        {typeof Icon !== 'undefined'
          ? <Icon name="rings" size={24} color={(W && W.clay) || '#a87a4f'} strokeWidth={1.4} />
          : <div style={{ fontSize: 26 }}>◯</div>}
      </div>
      <div style={{
        fontFamily: "'Geist', system-ui, sans-serif", fontSize: 10, fontWeight: 600,
        letterSpacing: 2, textTransform: 'uppercase',
        color: (W && W.muted) || '#9d8d77', marginTop: 18,
      }}>WeddingOS</div>

      <div style={{
        fontFamily: "'Cormorant Garamond', Georgia, serif", fontSize: 38, fontWeight: 500,
        color: (W && W.ink) || '#1d1812', marginTop: 30, letterSpacing: -0.4, textAlign: 'center',
      }}>
        Welcome.
      </div>
      <div style={{
        fontFamily: "'Cormorant Garamond', Georgia, serif", fontStyle: 'italic', fontSize: 18,
        color: (W && W.ink2) || '#5a4d3c', marginTop: 12,
      }}>
        Let's activate your planner.
      </div>
      <div style={{
        fontFamily: "'Geist', sans-serif", fontSize: 13.5,
        color: (W && W.muted) || '#9d8d77',
        marginTop: 16, lineHeight: 1.55, maxWidth: 360, textAlign: 'center',
      }}>
        Enter the access code from your Etsy delivery. This becomes your private key · keep it safe.
      </div>

      {/* code input */}
      <div style={{ marginTop: 36, display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'nowrap' }}>
        <span style={{
          fontFamily: "'Geist Mono', monospace", fontSize: 18, fontWeight: 600,
          color: (W && W.muted) || '#9d8d77',
        }}>WO</span>
        <span style={{ color: (W && W.faint) || '#c5b89f', fontFamily: "'Geist Mono', monospace" }}>–</span>
        {[0,1,2].map(i => (
          <React.Fragment key={i}>
            <input
              ref={segRefs[i]}
              value={segs[i]}
              onChange={(e) => onSegChange(i, e.target.value)}
              onKeyDown={(e) => onSegKey(i, e)}
              placeholder="····"
              maxLength={4}
              autoCapitalize="characters"
              autoCorrect="off"
              spellCheck={false}
              inputMode="text"
              style={{
                width: 68, height: 48, border: '1px solid rgba(0,0,0,0.12)',
                borderRadius: 12, textAlign: 'center',
                fontFamily: "'Geist Mono', monospace", fontSize: 18, fontWeight: 600,
                letterSpacing: 2, color: (W && W.ink) || '#1d1812',
                background: '#fff', outline: 'none',
              }}
            />
            {i < 2 && <span style={{ color: (W && W.faint) || '#c5b89f', fontFamily: "'Geist Mono', monospace" }}>–</span>}
          </React.Fragment>
        ))}
      </div>

      {error && (
        <div style={{
          marginTop: 16, padding: '10px 14px', borderRadius: 8,
          background: '#fbe7e0', color: '#8a3a1e',
          fontSize: 12.5, lineHeight: 1.45, maxWidth: 360, textAlign: 'center',
        }}>
          {error}
        </div>
      )}

      <button
        onClick={tryActivate}
        disabled={!complete || busy}
        style={{
          marginTop: 22, width: '100%', maxWidth: 360, padding: '15px 0',
          background: complete && !busy ? ((W && W.ink) || '#1d1812') : 'rgba(0,0,0,0.05)',
          color: complete && !busy ? ((W && W.bg) || '#faf6ec') : ((W && W.faint) || '#c5b89f'),
          border: 'none', borderRadius: 999,
          cursor: complete && !busy ? 'pointer' : 'default',
          fontFamily: "'Geist', sans-serif", fontSize: 14, fontWeight: 500, letterSpacing: 0.3,
          transition: 'background 180ms ease',
        }}
      >
        {busy ? 'Activating…' : (complete ? 'Activate' : 'Enter code to continue')}
      </button>

      <div style={{
        marginTop: 20, textAlign: 'center',
        fontFamily: "'Geist', sans-serif", fontSize: 12,
        color: (W && W.muted) || '#9d8d77',
      }}>
        Can't find your code?
        <span style={{ color: (W && W.clay) || '#a87a4f', marginLeft: 6 }}>
          Check the Etsy receipt PDF
        </span>
      </div>

      <div style={{ flex: 1 }} />
      <div style={{
        fontFamily: "'Cormorant Garamond', serif", fontStyle: 'italic', fontSize: 13,
        color: (W && W.muted) || '#9d8d77', lineHeight: 1.5, textAlign: 'center', marginBottom: 8,
      }}>
        One purchase. Up to five devices.<br/>
        No accounts, no email, no tracking.
      </div>
    </div>
  );
}

// ─── Auto-sync bridge ─────────────────────────────────────────
// Mounted inside StoreProvider. Pushes the whole state blob to the Worker
// on debounced change. Silent · UI doesn't need to know.
function SyncBridge() {
  const { state } = useStore();
  const code      = typeof localStorage !== 'undefined' && localStorage.getItem(__LICENSE_KEY);
  const weddingId = typeof localStorage !== 'undefined' && localStorage.getItem(__WEDDING_ID_KEY);

  React.useEffect(() => {
    if (!wosEnabled() || !code || !weddingId || !state) return;
    const t = setTimeout(async () => {
      const res = await wosPushWedding(code, weddingId, state).catch(() => ({ ok: false }));
      if (res && res.ok) {
        const ts = Date.now();
        try { localStorage.setItem('weddingos.lastSyncedAt', String(ts)); } catch {}
        window.dispatchEvent(new CustomEvent('wos-synced', { detail: { ts } }));
      } else {
        window.dispatchEvent(new CustomEvent('wos-sync-failed'));
      }
    }, __PUSH_DEBOUNCE_MS);
    return () => clearTimeout(t);
  }, [state, code, weddingId]);

  return null;
}

Object.assign(window, { LicenseGate, SyncBridge });
