/* firebase-sync.jsx
 * Firebase Realtime DB integration for hanoi-trip.
 * Exposes:
 *   window.useSyncedState(path, defaultValue) → [value, setValue]
 *   window.SyncIndicator                       → small badge component
 *   window.useSyncStatus()                     → 'idle'|'syncing'|'offline'|'error'
 *
 * Uses Firebase v10 compat SDK loaded via CDN in travel-app.html
 * (so the global `firebase` namespace is available).
 */

const FIREBASE_CONFIG = {
  apiKey: "AIzaSyBSgI39uCQiqeCZnaEyMFDp0Qs9QX-6sEc",
  authDomain: "hanoi-trip-a62c3.firebaseapp.com",
  databaseURL: "https://hanoi-trip-a62c3-default-rtdb.asia-southeast1.firebasedatabase.app",
  projectId: "hanoi-trip-a62c3",
  storageBucket: "hanoi-trip-a62c3.firebasestorage.app",
  messagingSenderId: "1005157362942",
  appId: "1:1005157362942:web:96fa82aaa0185eb48dc9af",
};

const TRIP_ID = 'hanoi-2026-05-29';

/* Init Firebase once */
if (!firebase.apps.length) firebase.initializeApp(FIREBASE_CONFIG);
const db = firebase.database();

/* Enable on-disk persistence for offline mode */
try { firebase.database().goOnline(); } catch {}

/* One-time: clear old single-device localStorage keys (now superseded by fb_*) */
try {
  ['hn_days_v2','hn_exp_v2','hn_todos'].forEach(k => localStorage.removeItem(k));
} catch {}

/* ──────────────────────── Sync status pub/sub ──────────────────────── */
let __syncListeners = [];
let __syncState = navigator.onLine ? 'idle' : 'offline';
function setSyncState(s) {
  if (s === __syncState) return;
  __syncState = s;
  __syncListeners.forEach(fn => fn(s));
}
window.addEventListener('online',  () => setSyncState('idle'));
window.addEventListener('offline', () => setSyncState('offline'));

function useSyncStatus() {
  const [s, setS] = React.useState(__syncState);
  React.useEffect(() => {
    __syncListeners.push(setS);
    return () => { __syncListeners = __syncListeners.filter(f => f !== setS); };
  }, []);
  return s;
}

/* ──────────────────────── useSyncedState hook ──────────────────────── */
function useSyncedState(path, defaultValue) {
  // Hydrate from localStorage cache immediately (instant first render)
  const [value, setValueRaw] = React.useState(() => {
    try {
      const s = localStorage.getItem(`fb_${path}`);
      if (s) return JSON.parse(s);
    } catch {}
    return defaultValue;
  });

  const debounceRef    = React.useRef(null);
  const localEchoRef   = React.useRef(null);   // last JSON we ourselves wrote
  const firstReadRef   = React.useRef(true);

  React.useEffect(() => {
    const r = db.ref(`trips/${TRIP_ID}/${path}`);

    const cb = (snap) => {
      const v = snap.val();

      // First read: if remote empty, seed with our default (or local cache)
      if (firstReadRef.current && v === null) {
        firstReadRef.current = false;
        let seed = defaultValue;
        try {
          const cached = localStorage.getItem(`fb_${path}`);
          if (cached) seed = JSON.parse(cached);
        } catch {}
        r.set(seed).catch(e => console.warn(`Seed ${path} failed:`, e));
        return;
      }
      firstReadRef.current = false;
      if (v === null) return;

      // Skip echoes of our own writes
      const json = JSON.stringify(v);
      if (json === localEchoRef.current) return;

      setValueRaw(v);
      try { localStorage.setItem(`fb_${path}`, json); } catch {}
    };

    const errCb = (err) => {
      console.warn(`Firebase ${path} read error:`, err);
      setSyncState('error');
    };

    r.on('value', cb, errCb);
    return () => r.off('value', cb);
  }, [path]);

  const setValue = React.useCallback((nextOrFn) => {
    setValueRaw(prev => {
      const computed = typeof nextOrFn === 'function' ? nextOrFn(prev) : nextOrFn;
      const json = JSON.stringify(computed);
      localEchoRef.current = json;
      try { localStorage.setItem(`fb_${path}`, json); } catch {}

      // Debounced remote write (collapse rapid updates)
      if (debounceRef.current) clearTimeout(debounceRef.current);
      setSyncState('syncing');
      debounceRef.current = setTimeout(() => {
        db.ref(`trips/${TRIP_ID}/${path}`).set(computed)
          .then(() => { if (navigator.onLine) setSyncState('idle'); })
          .catch(err => { console.warn(`Write ${path} failed:`, err); setSyncState('error'); });
      }, 250);

      return computed;
    });
  }, [path]);

  return [value, setValue];
}

/* ──────────────────────── Sync indicator badge ──────────────────────── */
const SYNC_CFG = {
  idle:    { color:'#10B981', icon:'ri-cloud-line',         label:'已同步' },
  syncing: { color:'#F59E0B', icon:'ri-loader-4-line',      label:'同步中' },
  offline: { color:'#9CA3AF', icon:'ri-cloud-off-line',     label:'離線' },
  error:   { color:'#EF4444', icon:'ri-error-warning-line', label:'同步異常' },
};

function SyncIndicator() {
  const status = useSyncStatus();
  const [expanded, setExpanded] = React.useState(false);
  const cfg = SYNC_CFG[status] || SYNC_CFG.idle;

  // Auto-collapse 1.2s after status becomes idle / offline
  React.useEffect(() => {
    if (status === 'syncing' || status === 'error') {
      setExpanded(true);
      return;
    }
    if (expanded) {
      const t = setTimeout(() => setExpanded(false), 1200);
      return () => clearTimeout(t);
    }
  }, [status]);

  // Idle: minimal dot, almost invisible
  if (!expanded && status === 'idle') {
    return (
      <div onClick={()=>setExpanded(true)} style={{
        position:'absolute',top:'calc(env(safe-area-inset-top) + 8px)',right:10,zIndex:55,
        width:8,height:8,borderRadius:'50%',background:cfg.color,opacity:0.45,
        cursor:'pointer',transition:'opacity 0.3s'
      }} aria-label={cfg.label}/>
    );
  }
  // Offline: small grey dot
  if (!expanded && status === 'offline') {
    return (
      <div onClick={()=>setExpanded(true)} style={{
        position:'absolute',top:'calc(env(safe-area-inset-top) + 8px)',right:10,zIndex:55,
        width:8,height:8,borderRadius:'50%',background:cfg.color,opacity:0.75,
        cursor:'pointer',transition:'opacity 0.3s'
      }} aria-label={cfg.label}/>
    );
  }
  // Expanded: full pill (syncing / error / brief peek)
  return (
    <div onClick={()=>setExpanded(false)} style={{
      position:'absolute',top:'calc(env(safe-area-inset-top) + 6px)',right:10,zIndex:55,
      display:'flex',alignItems:'center',gap:4,
      background:'rgba(0,0,0,0.42)',backdropFilter:'blur(8px)',
      borderRadius:12,padding:'3px 8px',fontSize:10,color:'#fff',
      userSelect:'none',cursor:'pointer',transition:'all 0.2s'
    }}>
      <i className={cfg.icon} style={{
        color:cfg.color,fontSize:11,
        animation: status==='syncing'?'spin 1s linear infinite':'none'
      }}></i>
      <span>{cfg.label}</span>
    </div>
  );
}

window.useSyncedState = useSyncedState;
window.useSyncStatus  = useSyncStatus;
window.SyncIndicator  = SyncIndicator;
window.TRIP_ID        = TRIP_ID;

/* ──────────────────────── Live exchange rate ──────────────────────── */
/* Fetches 1 TWD → VND from open.er-api.com (free, no key, ECB-backed).
 * Returns { rate, vndPerTwd, fetchedAt } where rate = 1 VND in TWD
 * (matches the existing `exchangeRate` convention).
 */
async function fetchLiveRate() {
  const res = await fetch('https://open.er-api.com/v6/latest/TWD', { cache: 'no-store' });
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const j = await res.json();
  const vndPerTwd = j && j.rates && j.rates.VND;
  if (!isFinite(vndPerTwd) || vndPerTwd < 500 || vndPerTwd > 1500) {
    throw new Error(`匯率值異常 (${vndPerTwd})`);
  }
  return { rate: 1 / vndPerTwd, vndPerTwd, fetchedAt: Date.now() };
}

window.fetchLiveRate = fetchLiveRate;
