// ─────────────────────────────────────────────
// App.jsx  (V7)
// Views: home | list | detail
//
// V7 changes vs V6:
//   - Layout endpoint: /v7/layout instead of /v6/layout
//   - Schema: tables have _oid, tNum, tCode (not table)
//   - Layout is per-table, fetched on table select
//   - No allObjects — each view fetches its own data
// ─────────────────────────────────────────────

const DEFAULT_THEME = {
  nav_bg:        '#0f2744',
  nav_color:     '#ffffff',
  primary:       '#2563eb',
  accent:        '#f59e0b',
  font:          "'Segoe UI', sans-serif",
  font_size:     14,
  border_radius: 6,
  card_bg:       '#ffffff',
  page_bg:       '#f8fafc',
  logo_text:     'DO',
  label_size:    '10px',
  label_color:   '#64748b',
};
window.__DO_THEME__ = DEFAULT_THEME;

function App() {
  const [appName,      setAppName]      = React.useState('');
  const [appDesc,      setAppDesc]      = React.useState('');
  const [theme,        setTheme]        = React.useState(DEFAULT_THEME);
  const [settings,     setSettings]     = React.useState({ save:'manual', timeout:3000 });
  const [apiKeys,      setApiKeys]      = React.useState({});
  const [tables,       setTables]       = React.useState([]);
  const [homeReady,    setHomeReady]    = React.useState(false);
  const [inactive,     setInactive]     = React.useState(false);
  const [authReady,    setAuthReady]    = React.useState(false);
  const [jwt,          setJwt]          = React.useState(null);
  const [currentUser,  setCurrentUser]  = React.useState(null);
  const [error,        setError]        = React.useState(null);

  // View routing
  const [view,         setView]         = React.useState('home');
  const [currentTable, setCurrentTable] = React.useState(null); // { tNum, tCode, name, ... }
  const [openId,       setOpenId]       = React.useState(null);

  // Per-table layout cache
  const [tableLayouts, setTableLayouts] = React.useState({});

  // Found set
  const [foundSet,     setFoundSet]     = React.useState([]);
  const [foundTotal,   setFoundTotal]   = React.useState(0);
  const [totalRecords, setTotalRecords] = React.useState(0);

  // List state preserved across detail nav
  const [listOffset,   setListOffset]   = React.useState(0);
  const [listPageSize, setListPageSize] = React.useState(25);

  // Nav panel toggles
  const [searchOpen,  setSearchOpen]  = React.useState(false);
  const [profileOpen,    setProfileOpen]    = React.useState(false);
  const [adminUsersOpen, setAdminUsersOpen] = React.useState(false);

  // ── Invite token state ───────────────────────
  const [inviteStep,   setInviteStep]   = React.useState(null); // null|'set_password'|'error'
  const [inviteToken,  setInviteToken]  = React.useState(null);
  const [inviteEmail,  setInviteEmail]  = React.useState('');
  const [inviteError,  setInviteError]  = React.useState('');
  const [invitePw,     setInvitePw]     = React.useState('');
  const [invitePw2,    setInvitePw2]    = React.useState('');
  const [inviteSaving, setInviteSaving] = React.useState(false);

  // Detail state lifted for nav bar
  const [detailRecord,  setDetailRecord]  = React.useState(null);
  const [detailSaving,  setDetailSaving]  = React.useState(false);
  const [detailSaved,   setDetailSaved]   = React.useState(false);
  const [detailDirty,   setDetailDirty]   = React.useState(false);
  const [detailLoading, setDetailLoading] = React.useState(false);
  const [detailIsNew,   setDetailIsNew]   = React.useState(false);

  const detailRef = React.useRef(null);
  const listRef   = React.useRef(null);

  // ── Fetch client IP for DO dev panels ────────
  React.useEffect(() => {
    fetch(`${API_BASE}/v6/myip`)
      .then(r => r.json())
      .then(d => { window.__DO_CLIENT_IP__ = d.cf || d.forwarded?.split(',')[0]?.trim() || ''; })
      .catch(() => {});
  }, []);
  React.useEffect(() => {
    const params   = new URLSearchParams(window.location.search);
    const urlToken = params.get('token');

    if (urlToken) {
      // Strip token from URL immediately
      window.history.replaceState({}, '', window.location.pathname);

      setInviteToken(urlToken);
      apiFetch(`${API_BASE}/v6/auth/token/validate`, {
        method: 'POST',
        body: JSON.stringify({ app: APP_ID, token: urlToken })
      })
        .then(r => r.json())
        .then(data => {
          if (data.status === 'set_password') {
            setInviteEmail(data.email || '');
            setInviteStep('set_password');
          } else if (data.status === 'ok') {
            localStorage.setItem(window.jwtKey(), data.jwt);
            setJwt(data.jwt);
            setCurrentUser(data.user);
            setInviteStep(null);
          } else {
            setInviteError(data.message || 'Invite link expired or already used.');
            setInviteStep('error');
          }
        })
        .catch(() => {
          setInviteError('Could not validate invite link. Please log in manually.');
          setInviteStep('error');
        })
        .finally(() => setAuthReady(true));
      return;
    }

    const stored = localStorage.getItem(window.jwtKey());
    if (!stored) { setAuthReady(true); return; }
    fetch(`${API_BASE}/v6/auth/session`, {
      headers: { Authorization: `Bearer ${stored}` }
    })
      .then(r => r.json())
      .then(data => {
        if (data.status === 'ok') {
          setJwt(stored);
          setCurrentUser(data.user);
        } else {
          localStorage.removeItem(window.jwtKey());
        }
      })
      .catch(() => localStorage.removeItem(window.jwtKey()))
      .finally(() => setAuthReady(true));
  }, []);

  // ── Fetch home layout once authenticated ─────
  React.useEffect(() => {
    if (!jwt) return;
    apiFetch(`${API_BASE}/v7/layout?app=${APP_ID}`)
      .then(r => r.json())
      .then(data => {
        if (data.status === 'inactive') { setInactive(true); setHomeReady(true); return; }
        if (data.status !== 'ok') throw new Error(data.message);
        setAppName(data.appName || '');
        setAppDesc(data.appDesc || '');
        if (data.appName) document.title = data.appName;
        setTables(data.tables || []);
        if (data.settings) setSettings({ save:'manual', timeout:3000, ...data.settings });
        if (data.apiKeys)  setApiKeys(data.apiKeys);
        if (data.theme) {
          const merged = { ...DEFAULT_THEME, ...data.theme };
          setTheme(merged);
          window.__DO_THEME__ = merged;
          // Apply density class to body
          const dc = { XS:'do-xs', S:'do-s', M:'do-m', L:'do-l', XL:'do-xl' };
          document.body.classList.remove('do-xs','do-s','do-m','do-l','do-xl');
          document.body.classList.add(dc[merged.density || 'M'] || 'do-m');
        }
        setHomeReady(true);
      })
      .catch(err => setError(err.message));
  }, [jwt]);

  // ── Fetch per-table layout (cached) ──────────
  async function fetchTableLayout(tCode) {
    if (tableLayouts[tCode]) return tableLayouts[tCode];
    try {
      const res  = await apiFetch(`${API_BASE}/v7/layout?app=${APP_ID}&table=${tCode}`);
      const data = await res.json();
      if (data.status !== 'ok') throw new Error(data.message);
      setTableLayouts(prev => ({ ...prev, [tCode]: data }));
      return data;
    } catch(err) {
      console.error('fetchTableLayout error:', err.message);
      return null;
    }
  }

  // ── Navigation ───────────────────────────────
  const scrollTop = () => window.scrollTo({ top:0, behavior:'instant' });

  const goHome = () => { setView('home'); scrollTop(); };

  const goList = async (tableRow) => {
    const t = tableRow || currentTable;
    if (!t) return;
    setCurrentTable(t);
    await fetchTableLayout(t.tCode);
    setView('list');
    setSearchOpen(false);
    scrollTop();
  };

  const goDetail = async (id, tableRow) => {
    const t = tableRow || currentTable;
    if (!t) return;
    setCurrentTable(t);
    await fetchTableLayout(t.tCode);
    setOpenId(id);
    setView('detail');
    setSearchOpen(false);
    scrollTop();
  };

  const handleTableSelect = async (tableRow) => {
    const link = tableRow.link || 'list';
    if (link === 'detail') {
      await goDetail(null, tableRow);
    } else {
      await goList(tableRow);
    }
  };

  const handleOpen = (r_auto, set, total) => {
    setFoundSet(set || []);
    setFoundTotal(total || 0);
    goDetail(r_auto);
  };

  const handleFoundSetChange = (set, total, pageSize, offset) => {
    setFoundSet(set);
    setFoundTotal(total);
    if (pageSize !== undefined) setListPageSize(pageSize);
    if (offset   !== undefined) setListOffset(offset);
  };

  // ── Nav bar delegates ────────────────────────
  const handleNavFirst     = () => detailRef.current?.goFirst();
  const handleNavPrev      = () => detailRef.current?.goPrev()  || listRef.current?.goPrev();
  const handleNavNext      = () => detailRef.current?.goNext()  || listRef.current?.goNext();
  const handleNavLast      = () => detailRef.current?.goLast();
  const handleNavRefresh   = () => detailRef.current?.refresh() || listRef.current?.refresh();
  const userRole           = (currentUser?.role || currentUser?.userType || 'user');
  const isGuest            = userRole === 'guest';
  const handleNavNew       = () => { if (isGuest) return; if (view === 'list') goDetail('new'); else detailRef.current?.newRecord(); };
  const handleNavSave      = () => detailRef.current?.save();
  const handleNavDelete    = () => detailRef.current?.deleteRecord();
  const handleNavDuplicate = () => detailRef.current?.duplicate();

  const currentPos = view === 'detail' && detailRecord && foundSet?.length > 0
    ? foundSet.indexOf(detailRecord.r_auto) : -1;
  const hasPrev = view === 'detail' ? currentPos > 0 : listOffset > 0;
  const hasNext = view === 'detail'
    ? currentPos >= 0 && currentPos < foundSet.length - 1
    : (listOffset + listPageSize) < (foundTotal || totalRecords);

  // ── Current table layout ─────────────────────
  const tableLayout = currentTable ? tableLayouts[currentTable.tCode] : null;

  // ── Early returns ────────────────────────────
  if (error) return (
    <div style={{ padding:40, color:'#991b1b', fontFamily:'sans-serif' }}>❌ {error}</div>
  );
  if (!authReady) return (
    <div style={{ padding:40, color:'#64748b', fontFamily:'sans-serif' }}>Loading…</div>
  );

  // ── Invite: set password screen ───────────────
  if (inviteStep === 'set_password') {
    const navBg    = theme.nav_bg    || '#0f2744';
    const navColor = theme.nav_color || '#ffffff';
    async function activateAccount(e) {
      e.preventDefault();
      if (invitePw.length < 6) return alert('Password must be at least 6 characters');
      if (invitePw !== invitePw2) return alert('Passwords do not match');
      setInviteSaving(true);
      try {
        const res  = await apiFetch(`${API_BASE}/v6/auth/token/activate`, {
          method: 'POST',
          body: JSON.stringify({ app: APP_ID, token: inviteToken, password: invitePw })
        });
        const data = await res.json();
        if (data.status !== 'ok') throw new Error(data.message);
        localStorage.setItem(window.jwtKey(), data.jwt);
        setJwt(data.jwt);
        setCurrentUser(data.user);
        setInviteStep(null);
      } catch(err) {
        setInviteError(err.message || 'Could not activate account.');
      }
      setInviteSaving(false);
    }
    return (
      <div style={{ minHeight:'100vh', display:'flex', alignItems:'center',
                    justifyContent:'center', background: theme.page_bg || '#f8fafc',
                    fontFamily: theme.font || "'Segoe UI', sans-serif" }}>
        <div style={{ background:'#fff', borderRadius:12, width:'100%', maxWidth:420,
                      boxShadow:'0 8px 32px rgba(0,0,0,0.12)', overflow:'hidden' }}>
          <div style={{ background:navBg, color:navColor, padding:'28px 32px' }}>
            <div style={{ fontSize:22, fontWeight:700, marginBottom:4 }}>
              Welcome to {appName || 'DataObjects'}
            </div>
            <div style={{ fontSize:14, opacity:0.8 }}>
              {inviteEmail && <>You've been invited as <strong>{inviteEmail}</strong>.<br/></>}
              Set a password to activate your account.
            </div>
          </div>
          <form onSubmit={activateAccount} style={{ padding:'28px 32px' }}>
            {inviteError && (
              <div style={{ background:'#fef2f2', color:'#991b1b', padding:'10px 12px',
                            borderRadius:6, fontSize:13, marginBottom:16 }}>
                {inviteError}
              </div>
            )}
            <label style={{ display:'block', fontSize:11, fontWeight:700, color:'#94a3b8',
                            textTransform:'uppercase', letterSpacing:'0.06em', marginBottom:5 }}>
              New Password
            </label>
            <input type="password" value={invitePw} onChange={e => setInvitePw(e.target.value)}
              placeholder="At least 6 characters" autoFocus
              style={{ width:'100%', padding:'10px 12px', border:'1.5px solid #e2e8f0',
                       borderRadius:7, fontSize:14, marginBottom:14, boxSizing:'border-box',
                       outline:'none', fontFamily:'inherit' }} />
            <label style={{ display:'block', fontSize:11, fontWeight:700, color:'#94a3b8',
                            textTransform:'uppercase', letterSpacing:'0.06em', marginBottom:5 }}>
              Confirm Password
            </label>
            <input type="password" value={invitePw2} onChange={e => setInvitePw2(e.target.value)}
              placeholder="Confirm password"
              style={{ width:'100%', padding:'10px 12px', border:'1.5px solid #e2e8f0',
                       borderRadius:7, fontSize:14, marginBottom:20, boxSizing:'border-box',
                       outline:'none', fontFamily:'inherit' }} />
            <button type="submit" disabled={inviteSaving}
              style={{ width:'100%', padding:'12px', background:navBg, color:navColor,
                       border:'none', borderRadius:8, fontSize:15, fontWeight:700,
                       cursor:'pointer', fontFamily:'inherit' }}>
              {inviteSaving ? 'Activating…' : 'Activate Account →'}
            </button>
          </form>
        </div>
      </div>
    );
  }

  // ── Invite: error screen ──────────────────────
  if (inviteStep === 'error') return (
    <div style={{ minHeight:'100vh', display:'flex', alignItems:'center',
                  justifyContent:'center', background: theme.page_bg || '#f8fafc',
                  fontFamily: theme.font || "'Segoe UI', sans-serif" }}>
      <div style={{ textAlign:'center', maxWidth:380, padding:'40px 32px',
                    background:'#fff', borderRadius:12, boxShadow:'0 4px 24px rgba(0,0,0,0.1)' }}>
        <div style={{ fontSize:40, marginBottom:16 }}>🔗</div>
        <div style={{ fontSize:18, fontWeight:700, marginBottom:8 }}>Invite Link Issue</div>
        <div style={{ fontSize:14, color:'#64748b', marginBottom:24 }}>{inviteError}</div>
        <button onClick={() => setInviteStep(null)}
          style={{ padding:'10px 24px', background:'#0f2744', color:'#fff', border:'none',
                   borderRadius:7, fontSize:14, fontWeight:600, cursor:'pointer' }}>
          Go to Login
        </button>
      </div>
    </div>
  );

  if (!jwt) return (
    <DOLogin appId={APP_ID} theme={theme} appSettings={settings}
      appName={window.APP_NAME || 'DataObjects'} onLogin={(token, user) => {
        localStorage.setItem(window.jwtKey(), token);
        setJwt(token); setCurrentUser(user);
      }} />
  );
  if (!homeReady) return (
    <div style={{ padding:40, color:'#64748b', fontFamily:'sans-serif' }}>Loading…</div>
  );
  if (inactive) return (
    <div style={{ minHeight:'100vh', display:'flex', alignItems:'center',
                  justifyContent:'center', background: theme.page_bg,
                  fontFamily: theme.font }}>
      <div style={{ textAlign:'center', maxWidth:400, padding:'40px 32px',
                    background:'#fff', borderRadius:16, border:'1px solid #e2e8f0' }}>
        <div style={{ fontSize:48, marginBottom:20 }}>🔒</div>
        <div style={{ fontSize:20, fontWeight:700, color:'#0f1923', marginBottom:10 }}>
          App Unavailable
        </div>
        <div style={{ fontSize:14, color:'#64748b', lineHeight:1.6 }}>
          This application is currently offline.<br/>
          Please contact your administrator.
        </div>
      </div>
    </div>
  );

  const fs = theme.font_size ? `${theme.font_size}px` : '14px';
  const maxW = settings.container === 'fluid' ? '100%' : 1200;

  return (
    <div>
      <DONavBar
        view={view}
        allObjects={tables.map(t => ({
          ...t,
          group: 'schema', type: 'table',
          table: t.tCode, _table: t.tNum,
          o_id: t._oid, status: '1',
        }))}
        tables={tables}
        currentTable={currentTable?.tCode || currentTable?.code || ''}
        theme={theme}
        record={detailRecord}
        foundSet={foundSet}
        foundTotal={foundTotal}
        totalRecords={totalRecords}
        offset={listOffset}
        pageSize={listPageSize}
        saveMode={settings.save}
        saving={detailSaving}
        saved={detailSaved}
        dirty={detailDirty}
        loading={detailLoading}
        isNew={detailIsNew}
        hasPrev={hasPrev}
        hasNext={hasNext}
        onHome={goHome}
        onTableSelect={tCode => {
          const t = tables.find(t => t.tCode === tCode || t.code === tCode);
          if (t) handleTableSelect(t);
        }}
        onBack={() => goList()}
        onNew={handleNavNew}
        onRefresh={handleNavRefresh}
        onSearch={() => setSearchOpen(!searchOpen)}
        onSave={handleNavSave}
        onFirst={handleNavFirst}
        onPrev={handleNavPrev}
        onNext={handleNavNext}
        onLast={handleNavLast}
        onDelete={handleNavDelete}
        onDuplicate={handleNavDuplicate}
        currentUser={currentUser}
        onLogout={() => { localStorage.removeItem(window.jwtKey()); setJwt(null); setCurrentUser(null); }}
        onProfileOpen={() => setProfileOpen(true)}
        onAdminUsersOpen={() => setAdminUsersOpen(true)}
        userRole={userRole}
        isGuest={isGuest}
      />

      <div style={{ maxWidth: maxW, margin:'20px auto', padding:'0 20px',
                    background: theme.page_bg, fontFamily: theme.font,
                    fontSize: fs }}>

        {/* ── HOME ── */}
        {view === 'home' && (
          <DOHomePage
            tables={tables}
            theme={theme}
            appName={appName || window.APP_NAME || ''}
            appDesc={appDesc}
            onSelect={handleTableSelect}
          />
        )}

        {/* ── LIST VIEW ── */}
        {view === 'list' && currentTable && (
          <DOListView
            ref={listRef}
            tableRow={currentTable}
            layoutData={tableLayout}
            theme={theme}
            searchOpen={searchOpen}
            onSearchClose={() => setSearchOpen(false)}
            onOpen={handleOpen}
            onFoundSetChange={handleFoundSetChange}
            onTotalRecords={total => setTotalRecords(total)}
          />
        )}

        {/* ── DETAIL VIEW ── */}
        {view === 'detail' && currentTable && (
          <DODetailView
            ref={detailRef}
            tableRow={currentTable}
            layoutData={tableLayout}
            appSettings={settings}
            apiKeys={apiKeys}
            theme={theme}
            userRole={currentUser?.role || currentUser?.userType || 'user'}
            startId={openId === 'new' ? null : openId}
            isNewRecord={openId === 'new'}
            searchOpen={searchOpen}
            onSearchClose={() => setSearchOpen(false)}
            foundSet={foundSet}
            foundTotal={foundTotal}
            onFoundSetChange={handleFoundSetChange}
            onBack={() => goList()}
            onTableSelect={handleTableSelect}
            onRecordChange={setDetailRecord}
            onSavingChange={setDetailSaving}
            onSavedChange={setDetailSaved}
            onDirtyChange={setDetailDirty}
            onLoadingChange={setDetailLoading}
            onNewChange={setDetailIsNew}
          />
        )}
      </div>

      {profileOpen && typeof DOProfile !== 'undefined' && (
        <DOProfile
          user={currentUser}
          jwt={localStorage.getItem(window.jwtKey())}
          appId={APP_ID}
          onClose={() => setProfileOpen(false)}
          onUserUpdate={u => setCurrentUser(u)}
        />
      )}
      {adminUsersOpen && typeof DOAdminUsers !== 'undefined' && (
        <DOAdminUsers
          appId={APP_ID}
          jwt={localStorage.getItem(window.jwtKey())}
          theme={theme}
          onClose={() => setAdminUsersOpen(false)}
        />
      )}
    </div>
  );
}
