// Main app shell — state is a cache of api.* responses; every mutation
// goes through the api layer (see api.js for the full endpoint contract).
// Filter, sort, and search stay client-side as designed.

function App() {
  const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
    "grouping": "tabs"
  }/*EDITMODE-END*/;
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);

  // ── Authentication ─────────────────────────────────────────────────────
  const [currentUser, setCurrentUser] = React.useState(null);
  const [loginError, setLoginError] = React.useState(null);
  const [loginLoading, setLoginLoading] = React.useState(false);

  const [route, setRoute] = React.useState('dashboard');
  const [role, setRoleState] = React.useState('admin');
  const [search, setSearch] = React.useState('');
  const [toasts, setToasts] = React.useState([]);
  const [subsFilter, setSubsFilter] = React.useState({ status: 'all', clientId: '', nonce: 0 });
  const [selectedClientId, setSelectedClientId] = React.useState(null);
  const [profileTabRequest, setProfileTabRequest] = React.useState(null);
  const [providerKindRequest, setProviderKindRequest] = React.useState(null);

  // ── Data cache ─────────────────────────────────────────────────────────
  const [domains, setDomains] = React.useState([]);
  const [servers, setServers] = React.useState([]);
  const [clients, setClients] = React.useState([]);
  const [employees, setEmployees] = React.useState([]);
  const [providers, setProviders] = React.useState([]);
  const [approvals, setApprovals] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [bootError, setBootError] = React.useState(null);

  // Modal state
  const [editing, setEditing] = React.useState(null);
  const [viewing, setViewing] = React.useState(null);

  const toast = (msg, tone) => {
    const id = Date.now() + Math.random();
    setToasts(prev => [...prev, { id, msg, tone }]);
    setTimeout(() => setToasts(prev => prev.filter(x => x.id !== id)), 3000);
  };

  // ── Authentication flow ────────────────────────────────────────────────
  const handleLogin = async (email, password) => {
    setLoginLoading(true);
    setLoginError(null);
    try {
      const result = await api.auth.login({ email, password });
      api.config.token = result.token;
      localStorage.setItem('auth_token', result.token);
      console.log('[AUTH] Token set to:', result.token.substring(0, 20) + '... CONFIG.token is now:', api.config.token ? api.config.token.substring(0, 20) + '...' : 'null');
      setCurrentUser(result.user);
      setRoleState(result.user.role.toLowerCase());
      setLoginLoading(false);
      toast(`Welcome back, ${result.user.name}!`);
    } catch (err) {
      if (err.code === 'account-inactive') {
        setLoginError('Your account is inactive. Please contact your administrator.');
      } else if (err.code === 'invalid-credentials') {
        setLoginError('Invalid email or password');
      } else {
        setLoginError(err.message || 'Login failed');
      }
      setLoginLoading(false);
    }
  };

  const handleLogout = async () => {
    await api.auth.logout();
    setCurrentUser(null);
    setDomains([]);
    setServers([]);
    setClients([]);
    setEmployees([]);
    setProviders([]);
    setApprovals([]);
    setRoute('dashboard');
    toast('Logged out');
  };

  // ── Boot: fetch everything in parallel ────────────────────────────────
  const reloadAll = React.useCallback(async () => {
    try {
      const [d, s, c, e, p, a] = await Promise.all([
        api.domains.list(),
        api.servers.list(),
        api.clients.list(),
        api.employees.list(),
        api.providers.list(),
        api.approvals.list(),
      ]);
      setDomains(d); setServers(s); setClients(c);
      setEmployees(e); setProviders(p); setApprovals(a);
    } catch (err) {
      setBootError(err.message || 'Failed to load workspace');
    } finally {
      setLoading(false);
    }
  }, []);

  React.useEffect(() => {
    (async () => {
      try {
        const token = localStorage.getItem('auth_token');
        if (!token) {
          setLoading(false);
          return;
        }

        const session = await api.auth.session();
        if (session && session.user && session.user.status === 'active') {
          setCurrentUser(session.user);
          setRoleState(session.user.role.toLowerCase());
          await reloadAll();
        } else if (session && session.user && session.user.status !== 'active') {
          // User session exists but account is inactive
          setCurrentUser(null);
          localStorage.removeItem('auth_token');
          setLoading(false);
          setLoginError('Your account has been deactivated. Please contact your administrator.');
        } else {
          // No session data, but keep loading to try
          setLoading(false);
        }
      } catch (e) {
        // Don't clear token on error - just fail to load
        setLoading(false);
      }
    })();
  }, [reloadAll]);

  // Load data when user logs in
  React.useEffect(() => {
    if (currentUser) {
      reloadAll();
    }
  }, [currentUser]);

  // Re-fetch helpers — refresh only the affected slice
  const refreshDomains = async () => setDomains(await api.domains.list());
  const refreshServers = async () => setServers(await api.servers.list());
  const refreshApprovals = async () => setApprovals(await api.approvals.list());
  const refreshEmployees = async () => setEmployees(await api.employees.list());
  const refreshProviders = async () => setProviders(await api.providers.list());

  // ── Role switching (mock-only: tells the api which user is acting) ────
  const setRole = (newRole) => {
    setRoleState(newRole);
    if (newRole === 'admin') {
      api.auth._setMockUser({ id: 'e1', name: 'Aanya Reddy', email: 'aanya@hostops.in', role: 'Admin' });
    } else {
      api.auth._setMockUser({ id: 'e2', name: 'Karan Mehta', email: 'karan@hostops.in', role: 'Employee' });
    }
  };

  // ── Approval / mutation flow ──────────────────────────────────────────
  // The api layer decides per role whether to apply immediately or queue
  // an approval. The component just inspects the resolved value.
  const handleMutationResult = (result) => {
    if (result?.status === 'pending-approval') {
      toast('Submitted for admin approval', 'pending');
    }
  };

  const onSaveFromForm = async (form, diff) => {
    try {
      if (editing.mode === 'create') {
        const out = await (editing.kind === 'domain'
          ? api.domains.create(form)
          : api.servers.create(form));
        if (out?.status !== 'pending-approval') toast(`Created ${editing.kind === 'domain' ? form.name : form.label}`);
        else handleMutationResult(out);
      } else {
        const out = await (editing.kind === 'domain'
          ? api.domains.update(editing.entity.id, diff)
          : api.servers.update(editing.entity.id, diff));
        if (out?.status !== 'pending-approval') toast(`Updated ${editing.kind === 'domain' ? editing.entity.name : editing.entity.label}`);
        else handleMutationResult(out);
      }
      await Promise.all([refreshDomains(), refreshServers(), refreshApprovals()]);
      setEditing(null);
    } catch (err) {
      toast(err.message || 'Save failed', 'danger');
    }
  };

  const onWithdraw = async () => {
    if (!editing?.entity) return;
    const pending = approvals.find(a => a.entity === editing.kind && a.entityId === editing.entity.id);
    if (!pending) { setEditing(null); return; }
    try {
      await api.approvals.withdraw(pending.id);
      await refreshApprovals();
      toast('Pending request withdrawn');
      setEditing(null);
    } catch (err) {
      toast(err.message || 'Withdraw failed', 'danger');
    }
  };

  const onApprove = async (a) => {
    try {
      await api.approvals.approve(a.id);
      await Promise.all([refreshDomains(), refreshServers(), refreshApprovals()]);
      toast('Approved & applied');
    } catch (err) { toast(err.message || 'Approve failed', 'danger'); }
  };
  const onReject = async (a) => {
    try {
      await api.approvals.reject(a.id);
      await refreshApprovals();
      toast('Request rejected', 'danger');
    } catch (err) { toast(err.message || 'Reject failed', 'danger'); }
  };

  const onDelete = async (entity) => {
    const isDomain = !!domains.find(d => d.id === entity.id);
    console.log('[APP] onDelete called for:', isDomain ? 'domain' : 'server', entity.id, 'CONFIG.token:', api.config.token ? api.config.token.substring(0, 20) + '...' : 'null');
    try {
      await (isDomain ? api.domains.remove(entity.id) : api.servers.remove(entity.id));
      await Promise.all([refreshDomains(), refreshServers(), refreshApprovals()]);
      toast('Deleted', 'danger');
      setEditing(null);
    } catch (err) {
      console.error('[APP] Delete error:', err.message);
      toast(err.message || 'Delete failed', 'danger');
    }
  };

  const onRenew = async (entity) => {
    const isDomain = !!domains.find(d => d.id === entity.id);
    try {
      const out = await (isDomain ? api.domains.renew(entity.id) : api.servers.renew(entity.id));
      if (out?.status === 'pending-approval') toast('Renewal submitted for approval', 'pending');
      else toast('Marked renewed (+1 year)');
      await Promise.all([refreshDomains(), refreshServers(), refreshApprovals()]);
    } catch (err) { toast(err.message || 'Renew failed', 'danger'); }
  };

  // ── Client operations ─────────────────────────────────────────────────
  const clientOps = {
    add: async (data) => {
      await api.clients.create(data);
      await refreshDomains();
      await refreshServers();
      setClients(await api.clients.list());
      toast('Client added');
    },
    edit: async (id, data) => {
      await api.clients.update(id, data);
      setClients(await api.clients.list());
      toast('Client updated');
    },
    delete: async (idOrIds) => {
      const ids = Array.isArray(idOrIds) ? idOrIds : [idOrIds];
      for (const id of ids) {
        try {
          await api.clients.remove(id);
        } catch (err) {
          toast(`Failed to delete client: ${err.message}`, 'danger');
        }
      }
      await refreshDomains();
      await refreshServers();
      setClients(await api.clients.list());
      toast(`${ids.length} client${ids.length === 1 ? '' : 's'} deleted`, 'danger');
    },
  };

  // ── Employees mutations (wrap setEmployees for child components) ──────
  const employeeOps = {
    list:   refreshEmployees,
    invite: async (data) => { await api.employees.invite(data); await refreshEmployees(); toast('Invitation sent'); },
    remove: async (id, name) => { await api.employees.remove(id); await refreshEmployees(); toast(`${name} removed`, 'danger'); },
  };
  // Legacy: ProfileScreen accepts setEmployees(updater). Wrap it to call the API.
  // Children call this as `setEmployees(prev => prev.filter(...))`. We diff and translate to API ops.
  const setEmployeesAPI = async (updaterOrValue) => {
    const next = typeof updaterOrValue === 'function' ? updaterOrValue(employees) : updaterOrValue;
    const removed = employees.filter(e => !next.find(n => n.id === e.id));
    for (const r of removed) {
      try { await api.employees.remove(r.id); } catch (err) { toast(err.message, 'danger'); }
    }
    setEmployees(next);
  };

  const setProvidersAPI = async (updaterOrValue) => {
    const next = typeof updaterOrValue === 'function' ? updaterOrValue(providers) : updaterOrValue;
    // Diff: detect adds, edits, status flips
    const prev = providers;
    for (const n of next) {
      const p = prev.find(x => x.id === n.id);
      if (!p) { try { await api.providers.create(n); } catch (e) { toast(e.message, 'danger'); } continue; }
      if (p.status === 'active' && n.status === 'archived') { try { await api.providers.archive(n.id); } catch (e) { toast(e.message, 'danger'); } continue; }
      if (p.status === 'archived' && n.status === 'active') { try { await api.providers.unarchive(n.id); } catch (e) { toast(e.message, 'danger'); } continue; }
      // Plain edit
      const fields = ['name', 'website', 'accountEmail', 'supportPhone', 'notes'];
      const diff = {};
      for (const f of fields) if (p[f] !== n[f]) diff[f] = n[f];
      if (Object.keys(diff).length) { try { await api.providers.update(n.id, diff); } catch (e) { toast(e.message, 'danger'); } }
    }
    await refreshProviders();
  };

  // ── Navigation ────────────────────────────────────────────────────────
  const goTo = (target, opts) => {
    setRoute(target);
    setSearch('');
    setSelectedClientId(null);
    if (target === 'subscriptions' && opts) {
      setSubsFilter(prev => ({
        status: opts.status || 'all',
        clientId: opts.clientId || '',
        nonce: prev.nonce + 1,
      }));
    }
  };

  // ── Modal entry points ────────────────────────────────────────────────
  const openEdit = (kindOrEntity, maybeEntity) => {
    let kind, entity;
    if (typeof kindOrEntity === 'string') { kind = kindOrEntity; entity = maybeEntity; }
    else {
      entity = kindOrEntity;
      kind = domains.find(d => d.id === entity.id) ? 'domain' : 'server';
    }
    setEditing({ mode: 'edit', kind, entity });
  };
  const openAdd = (kind) => setEditing({ mode: 'create', kind, entity: null });
  const openView = (entity) => {
    const kind = domains.find(d => d.id === entity.id) ? 'domain' : 'server';
    setViewing({ kind, entity });
  };

  const pendingForEditing = editing?.entity
    ? (approvals.find(a => a.kind === 'edit' && a.entity === editing.kind && a.entityId === editing.entity.id)?.changes || null)
    : null;

  const titles = { dashboard: 'Dashboard', subscriptions: 'Subscriptions', approvals: 'Approvals', clients: 'Clients', employees: 'Employees' };

  const filteredApprovals = approvals.filter(a => {
    if (!search) return true;
    const s = search.toLowerCase();
    const e = a.entity === 'domain' ? domains.find(d => d.id === a.entityId) : servers.find(s2 => s2.id === a.entityId);
    const blob = [a.by, a.entity, a.note, e?.name, e?.label, a.payload?.name, a.payload?.label].filter(Boolean).join(' ').toLowerCase();
    return blob.includes(s);
  });

  // ── Render ────────────────────────────────────────────────────────────
  if (loading) {
    return (
      <div style={{ position: 'fixed', inset: 0, display: 'grid', placeItems: 'center', background: 'var(--bg-subtle)' }}>
        <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12 }}>
          <div style={{ width: 36, height: 36, borderRadius: 8, overflow: 'hidden', display: 'grid', placeItems: 'center' }}><img src="favicon.ico" alt="Merida HostOps" style={{ width: 36, height: 36, objectFit: 'contain' }} /></div>
          <div style={{ color: 'var(--text-muted)', fontSize: 13 }}>Loading workspace…</div>
        </div>
      </div>
    );
  }

  if (bootError) {
    return (
      <div style={{ position: 'fixed', inset: 0, display: 'grid', placeItems: 'center' }}>
        <div className="card" style={{ padding: 24, maxWidth: 420 }}>
          <div className="notice red"><Icon name="alert" size={14}/><div><strong>Couldn't load workspace</strong><div>{bootError}</div></div></div>
          <button className="btn brand" style={{ marginTop: 12 }} onClick={() => { setBootError(null); setLoading(true); reloadAll(); }}>
            <Icon name="refresh" size={13}/> Try again
          </button>
        </div>
      </div>
    );
  }

  // Show login screen if not authenticated
  if (!currentUser) {
    return <LoginScreen onLogin={handleLogin} error={loginError} loading={loginLoading} />;
  }

  return (
    <div className="app">
      <AppBar
        route={route} setRoute={(r) => { setRoute(r); setSearch(''); setSelectedClientId(null); }}
        role={role} setRole={setRole}
        pendingCount={approvals.length}
        search={search} setSearch={setSearch}
        onLogout={handleLogout}
        currentUser={currentUser}
      />
      <main className="main">
        <div className="page">
          {route === 'dashboard' && (
            <DashboardScreen
              domains={domains} servers={servers} clients={clients} employees={employees}
              approvals={approvals} role={role}
              onGoTo={goTo}
              onEdit={(kind, e) => openEdit(kind, e)}
            />
          )}
          {(route === 'domains' || route === 'servers' || route === 'subscriptions') && (
            <SubscriptionsScreen
              kind={route === 'domains' ? 'domain' : route === 'servers' ? 'server' : undefined}
              domains={domains} servers={servers} clients={clients} approvals={approvals} role={role}
              providers={providers}
              onEdit={openEdit} onView={openView} onAdd={openAdd} onRenew={onRenew} onDelete={onDelete}
              toast={toast}
              reloadAll={reloadAll}
              onManageProviders={() => {
                setProfileTabRequest('providers');
                setProviderKindRequest(route === 'domains' ? 'domain' : 'server');
                setRoute('profile');
              }}
              grouping={t.grouping} search={search}
              onSetGrouping={(v) => setTweak('grouping', v)}
              initialStatusFilter={subsFilter.status}
              initialClientFilter={subsFilter.clientId}
              filterNonce={subsFilter.nonce}
            />
          )}
          {route === 'approvals' && (
            <ApprovalsScreen
              approvals={filteredApprovals} domains={domains} servers={servers} clients={clients} role={role}
              onApprove={onApprove} onReject={onReject}
            />
          )}
          {route === 'clients' && !selectedClientId && (
            <ClientsScreen
              clients={clients} domains={domains} servers={servers} approvals={approvals} role={role}
              onGoTo={goTo}
              onSelectClient={(id) => setSelectedClientId(id)}
              onAddClient={clientOps.add}
              onEditClient={clientOps.edit}
              onDeleteClient={clientOps.delete}
            />
          )}
          {route === 'clients' && selectedClientId && (
            <ClientDetailScreen
              client={clients.find(c => c.id === selectedClientId)}
              clients={clients}
              domains={domains} servers={servers} approvals={approvals} role={role}
              onBack={() => setSelectedClientId(null)}
              onEdit={openEdit}
              onView={openView}
              onRenew={onRenew}
              onAdd={openAdd}
              onEditClient={clientOps.edit}
              onDeleteClient={clientOps.delete}
            />
          )}
          {route === 'employees' && (
            role === 'admin'
              ? <ProfileScreen role={role} onChangeRole={setRole} employees={employees} setEmployees={setEmployeesAPI} providers={providers} setProviders={setProvidersAPI} domains={domains} servers={servers} toast={toast} />
              : <Empty icon="shield" title="Admins only" body="Team management is only visible to workspace admins." />
          )}
          {route === 'profile' && role === 'admin' && (
            <ProfileScreen role={role} onChangeRole={setRole} employees={employees} setEmployees={setEmployeesAPI} providers={providers} setProviders={setProvidersAPI} domains={domains} servers={servers} toast={toast}
              initialTab={profileTabRequest}
              initialProviderKind={providerKindRequest}
              onTabConsumed={() => { setProfileTabRequest(null); setProviderKindRequest(null); }}
              currentUser={currentUser}
              onCurrentUserChange={(u) => setCurrentUser(u)}
            />
          )}
          {route === 'profile' && role !== 'admin' && (
            <Empty icon="shield" title="Admins only" body="The profile page is only visible to workspace admins." />
          )}
          {route === 'settings' && role === 'admin' && (
            <SettingsScreen role={role} toast={toast} />
          )}
          {route === 'settings' && role !== 'admin' && (
            <Empty icon="shield" title="Admins only" body="Alert settings are only visible to workspace admins." />
          )}
        </div>
      </main>

      {editing && (
        <SubscriptionForm
          mode={editing.mode}
          kind={editing.kind}
          initial={editing.entity}
          pendingChanges={pendingForEditing}
          clients={clients}
          providers={providers.filter(p => p.status === 'active')}
          servers={servers}
          domains={domains}
          role={role}
          onClose={() => setEditing(null)}
          onSave={onSaveFromForm}
          onWithdraw={onWithdraw}
          onDelete={onDelete}
        />
      )}

      {viewing && (
        <DetailModal viewing={viewing} clients={clients} servers={servers} approvals={approvals}
          onClose={() => setViewing(null)}
          onEdit={() => { setEditing({ mode: 'edit', kind: viewing.kind, entity: viewing.entity }); setViewing(null); }}
        />
      )}

      <div className="toast-wrap">
        {toasts.map(t => (
          <div className="toast" key={t.id}>
            <Icon name={t.tone === 'pending' ? 'clock' : t.tone === 'danger' ? 'alert' : 'check'} size={14} />
            {t.msg}
          </div>
        ))}
      </div>

      <TweaksPanel>
        <TweakSection label="Layout" />
        <TweakSelect
          label="Subscriptions view"
          value={t.grouping}
          options={['tabs', 'all', 'domains', 'servers']}
          onChange={(v) => setTweak('grouping', v)}
        />
        <TweakSection label="Backend" />
        <TweakToggle
          label="Use mock backend"
          value={api.config.useMock}
          onChange={(v) => { api.config.useMock = v; }}
        />
        <TweakText
          label="API base URL"
          value={api.config.baseUrl}
          onChange={(v) => { api.config.baseUrl = v; }}
        />
        <TweakButton label="Reset mock data" onClick={() => { api._dev.reset(); reloadAll(); toast('Mock store reset'); }} />
      </TweaksPanel>
    </div>
  );
}

// Detail modal — read-only view (unchanged logic)
const DetailModal = ({ viewing, clients, servers, approvals, onClose, onEdit }) => {
  const { kind, entity } = viewing;
  const client = clients.find(c => c.id === entity.clientId);
  const pending = approvals.find(a => a.kind === 'edit' && a.entity === kind && a.entityId === entity.id);
  const fields = kind === 'domain'
    ? ['name', 'registrar', 'clientId', 'serverId', 'start', 'expiry', 'autoRenew', 'billedCost', 'purchasedCost']
    : ['label', 'provider', 'type', 'clientId', 'ip', 'panelDomain', 'os', 'region', 'cpu', 'ram', 'disk', 'start', 'expiry', 'autoRenew', 'billedCost', 'purchasedCost'];

  return (
    <Modal
      title={kind === 'domain' ? entity.name : entity.label}
      sub={`${kind === 'domain' ? 'Domain' : 'Server'} · ${client?.name || ''}`}
      onClose={onClose}
      footer={
        <>
          <button className="btn" onClick={onClose}>Close</button>
          <button className="btn brand" onClick={onEdit}><Icon name="edit" size={13}/> Edit</button>
        </>
      }
    >
      <div style={{ display: 'flex', gap: 6, marginBottom: 14 }}>
        <KindPill kind={kind} />
        <StatusBadge iso={entity.expiry} />
        {pending && <span className="badge amber dot">Pending edits</span>}
      </div>
      <div className="kv-grid">
        {fields.map(f => (
          <React.Fragment key={f}>
            <div className="k">{window.FIELD_LABELS[f]}</div>
            <div className="v">
              {window.renderFieldValue(entity, f, entity[f], clients, servers)}
              {pending?.changes?.[f] !== undefined && (
                <span style={{ marginLeft: 8 }}>
                  <Icon name="chevron" size={11}/>
                  <span className="diff-new" style={{ marginLeft: 6, fontSize: 11 }}>
                    {window.renderFieldValue(entity, f, pending.changes[f], clients, servers)}
                  </span>
                  <span className="badge amber" style={{ marginLeft: 6, fontSize: 10 }}>pending</span>
                </span>
              )}
            </div>
          </React.Fragment>
        ))}
      </div>
    </Modal>
  );
};

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
