const { useEffect, useMemo, useState } = React;

const CONFIG = window.ADMIN_CONFIG || {};
const STORAGE_KEY = 'ur_admin_auth_v1';
const DEFAULT_ADMIN_HASH = 'bc1daab2688c7bffd8f764426ac81028e8b4288f07522aa9dbf61555fae6c849';

const NAV_ITEMS = [
  { key: 'stats', title: '统计', copy: '总览供应、地区分布与同步状态', emoji: '◫' },
  { key: 'logs', title: '查看日志', copy: '审计每一轮 refresh 的结果与变化', emoji: '◭' },
];

function getSession() {
  try {
    return JSON.parse(sessionStorage.getItem(STORAGE_KEY) || 'null');
  } catch (error) {
    return null;
  }
}

function setSession(nextValue) {
  sessionStorage.setItem(STORAGE_KEY, JSON.stringify(nextValue));
}

function clearSession() {
  sessionStorage.removeItem(STORAGE_KEY);
}

function compactNumber(value) {
  if (!Number.isFinite(value)) return '0';
  return new Intl.NumberFormat('zh-CN', { maximumFractionDigits: value > 100 ? 0 : 1 }).format(value);
}

function formatPercent(value) {
  if (!Number.isFinite(value)) return '0%';
  return `${(value * 100).toFixed(value >= 0.1 ? 1 : 2)}%`;
}

function formatDateTime(value) {
  if (!value) return '未提供';
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) return value;
  return new Intl.DateTimeFormat('zh-CN', {
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
  }).format(date);
}

function formatRelative(value) {
  if (!value) return '未提供';
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) return value;
  const diffMinutes = Math.round((date.getTime() - Date.now()) / 60000);
  const formatter = new Intl.RelativeTimeFormat('zh-CN', { numeric: 'auto' });
  if (Math.abs(diffMinutes) < 60) return formatter.format(diffMinutes, 'minute');
  const diffHours = Math.round(diffMinutes / 60);
  if (Math.abs(diffHours) < 24) return formatter.format(diffHours, 'hour');
  const diffDays = Math.round(diffHours / 24);
  return formatter.format(diffDays, 'day');
}

function buildQuery(params = {}) {
  const search = new URLSearchParams();
  Object.entries(params).forEach(([key, value]) => {
    if (value === undefined || value === null || value === '') return;
    search.set(key, String(value));
  });
  const query = search.toString();
  return query ? `?${query}` : '';
}

async function apiFetch(path, params = {}) {
  const base = CONFIG.apiBase || 'https://api.repilot.jp';
  const token = CONFIG.apiToken || '';
  const url = base + path + buildQuery(params);
  const response = await fetch(url, {
    headers: token ? { Authorization: `Bearer ${token}` } : {},
  });
  if (!response.ok) {
    const text = await response.text();
    throw new Error(text || `API ${response.status}`);
  }
  return response.json();
}

async function sha256(value) {
  const data = new TextEncoder().encode(value);
  const hashBuffer = await crypto.subtle.digest('SHA-256', data);
  const hashArray = Array.from(new Uint8Array(hashBuffer));
  return hashArray.map((item) => item.toString(16).padStart(2, '0')).join('');
}

function toneForStatus(status) {
  if (status === 'success') return 'success';
  if (status === 'failed') return 'danger';
  return 'neutral';
}

function labelForStatus(status) {
  if (status === 'success') return '成功';
  if (status === 'failed') return '失败';
  if (status === 'running') return '进行中';
  return status || '未知';
}

function summarizeChanges(run) {
  const summary = run?.summary || {};
  return {
    listedRooms: summary.listedRoomCount || 0,
    delistedRooms: summary.delistedRoomCount || 0,
    listedProperties: summary.listedPropertyCount || 0,
    delistedProperties: summary.delistedPropertyCount || 0,
    activeRoomDelta: summary.activeRoomDelta || 0,
  };
}

function entryLabel(entry) {
  if (!entry) return '未命名记录';
  return entry.displayName || entry.propertyName || entry.roomName || entry.propertyKey || entry.roomKey || entry.canonicalRoomId || '未命名记录';
}

function renderTrigger(trigger) {
  if (trigger === 'manual') return '手动';
  if (trigger === 'scheduled') return '定时';
  return trigger || '未标注';
}

function getDefaultSetupActive() {
  const users = CONFIG.adminUsers || [];
  return users.length === 1 && users[0].username === 'admin' && users[0].passwordHash === DEFAULT_ADMIN_HASH;
}

function App() {
  const [session, setSessionState] = useState(getSession());
  const [view, setView] = useState('stats');
  const [statsState, setStatsState] = useState({ loading: false, error: '', data: null });
  const [logsState, setLogsState] = useState({
    loading: false,
    error: '',
    data: null,
    filters: {
      regionKey: '',
      trigger: '',
      changedOnly: false,
      since: '',
      until: '',
    },
  });

  const authed = Boolean(session?.username);

  useEffect(() => {
    if (!authed) return;
    refreshStats();
    refreshLogs(logsState.filters);
  }, [authed]);

  async function refreshStats() {
    setStatsState((current) => ({ ...current, loading: true, error: '' }));
    try {
      const [health, summary, regions, latestLog, allLogs] = await Promise.all([
        apiFetch('/health'),
        apiFetch('/api/v1/summary'),
        apiFetch('/api/v1/regions'),
        apiFetch('/api/v1/refresh-logs/latest'),
        apiFetch('/api/v1/refresh-logs'),
      ]);
      setStatsState({
        loading: false,
        error: '',
        data: {
          health,
          summary,
          regions: regions?.data || [],
          latestLog,
          logs: allLogs?.data || [],
        },
      });
    } catch (error) {
      setStatsState({ loading: false, error: error.message || '加载统计失败', data: null });
    }
  }

  async function refreshLogs(filters) {
    setLogsState((current) => ({ ...current, loading: true, error: '' }));
    try {
      const params = {
        regionKey: filters.regionKey,
        trigger: filters.trigger,
        changedOnly: filters.changedOnly ? 1 : '',
        since: filters.since ? new Date(filters.since).toISOString() : '',
        until: filters.until ? new Date(filters.until).toISOString() : '',
      };
      const response = await apiFetch('/api/v1/refresh-logs', params);
      setLogsState((current) => ({
        ...current,
        loading: false,
        error: '',
        data: response?.data || [],
      }));
    } catch (error) {
      setLogsState((current) => ({
        ...current,
        loading: false,
        error: error.message || '加载日志失败',
        data: [],
      }));
    }
  }

  function handleLogin(nextSession) {
    setSession(nextSession);
    setSessionState(nextSession);
  }

  function handleLogout() {
    clearSession();
    setSessionState(null);
  }

  if (!authed) {
    return <LoginScreen onLogin={handleLogin} />;
  }

  return (
    <div className="admin-shell">
      <Sidebar
        activeView={view}
        onChangeView={setView}
        onLogout={handleLogout}
        latestLog={statsState.data?.latestLog}
      />
      <main className="shell-main">
        <Topbar
          view={view}
          username={session.username}
          latestLog={statsState.data?.latestLog}
          health={statsState.data?.health}
          onRefresh={view === 'stats' ? refreshStats : () => refreshLogs(logsState.filters)}
          refreshing={view === 'stats' ? statsState.loading : logsState.loading}
        />

        {view === 'stats' ? (
          <StatsPage state={statsState} />
        ) : (
          <LogsPage
            state={logsState}
            regions={statsState.data?.regions || []}
            onChangeFilters={(filters) => setLogsState((current) => ({ ...current, filters }))}
            onApplyFilters={refreshLogs}
          />
        )}
      </main>
    </div>
  );
}

function Sidebar({ activeView, onChangeView, onLogout, latestLog }) {
  return (
    <aside className="sidebar">
      <div>
        <div className="brand-kicker">Repilot Internal</div>
        <h1 className="brand-title">UR Admin</h1>
        <p className="brand-copy">
          面向运营和维护的独立后台。查看供应、监控同步状态，并审计每一轮 refresh 的变化。
        </p>

        <div className="nav-list">
          {NAV_ITEMS.map((item) => (
            <button
              key={item.key}
              type="button"
              className={`nav-button${activeView === item.key ? ' is-active' : ''}`}
              onClick={() => onChangeView(item.key)}
            >
              <span className="nav-emoji">{item.emoji}</span>
              <span>
                <div className="nav-title">{item.title}</div>
                <div className="nav-copy">{item.copy}</div>
              </span>
            </button>
          ))}
        </div>
      </div>

      <div className="sidebar-foot">
        <div className="mini-panel">
          <div className="mini-panel-label">最近刷新状态</div>
          <div className="mini-panel-value">
            {latestLog ? `${labelForStatus(latestLog.status)} · ${renderTrigger(latestLog.trigger)}` : '等待数据'}
          </div>
        </div>
        <button type="button" className="btn btn-ghost" onClick={onLogout}>
          退出登录
        </button>
      </div>
    </aside>
  );
}

function Topbar({ view, username, latestLog, health, onRefresh, refreshing }) {
  const failed = latestLog?.status === 'failed' || Boolean(health?.lastRefreshError);
  return (
    <section className="topbar">
      <div>
        <h1>{view === 'stats' ? 'UR 运营统计台' : 'Refresh Logs 审计台'}</h1>
        <p>
          {view === 'stats'
            ? '聚焦当前 UR 全局供应、地区分布和同步健康度。'
            : '筛选每次 refresh 的执行结果，定位新增、下架和异常原因。'}
        </p>
      </div>

      <div className="topbar-actions">
        <span className="chip">
          <span className={`chip-dot ${failed ? 'is-danger' : 'is-success'}`}></span>
          {failed ? '最近一次刷新异常' : '最近一次刷新正常'}
        </span>
        <span className="chip">
          <span className="chip-dot"></span>
          管理员 {username}
        </span>
        <button type="button" className="btn btn-primary" onClick={onRefresh} disabled={refreshing}>
          {refreshing ? <span className="spin">◌</span> : '刷新数据'}
        </button>
      </div>
    </section>
  );
}

function StatsPage({ state }) {
  const data = state.data;

  const derived = useMemo(() => {
    if (!data) return null;
    const summary = data.summary?.summary || {};
    const regions = data.regions || [];
    const areas = data.summary?.areas || [];
    const latest = data.latestLog;

    const totalCatalogProperties = summary.totalCatalogProperties || 0;
    const totalProperties = summary.totalProperties || 0;
    const totalAvailableRooms = summary.totalAvailableRooms || 0;
    const totalRegions = summary.totalRegions || regions.length || 0;
    const totalAreasWithVacancy = summary.totalAreasWithVacancy || 0;
    const activeRegions = regions.filter((item) => (item.availableRoomCount || 0) > 0);
    const silentRegions = regions.filter((item) => (item.availableRoomCount || 0) === 0);
    const topRegions = [...regions]
      .sort((left, right) => (right.availableRoomCount || 0) - (left.availableRoomCount || 0))
      .slice(0, 8);
    const topAreas = [...areas]
      .sort((left, right) => (right.availableRoomCount || 0) - (left.availableRoomCount || 0))
      .slice(0, 6);
    const largestRegion = topRegions[0];
    const topThreeShare = totalAvailableRooms
      ? topRegions.slice(0, 3).reduce((sum, item) => sum + (item.availableRoomCount || 0), 0) / totalAvailableRooms
      : 0;

    return {
      totalCatalogProperties,
      totalProperties,
      totalAvailableRooms,
      totalRegions,
      totalAreasWithVacancy,
      activeRegions,
      silentRegions,
      topRegions,
      topAreas,
      largestRegion,
      vacancyCoverage: totalCatalogProperties ? totalProperties / totalCatalogProperties : 0,
      avgRoomsPerProperty: totalProperties ? totalAvailableRooms / totalProperties : 0,
      floorplanCoverage: totalAvailableRooms ? (summary.totalCachedFloorplanImages || 0) / totalAvailableRooms : 0,
      topThreeShare,
      latest,
      summary,
      health: data.health,
    };
  }, [data]);

  if (state.loading && !data) {
    return <div className="empty">正在加载统计数据…</div>;
  }

  if (state.error) {
    return <div className="error">{state.error}</div>;
  }

  if (!derived) {
    return <div className="empty">暂时没有可展示的数据。</div>;
  }

  const latestLabel = derived.latest
    ? `${labelForStatus(derived.latest.status)} · ${renderTrigger(derived.latest.trigger)}`
    : '暂无最近刷新记录';

  return (
    <div className="page-grid">
      {getDefaultSetupActive() ? (
        <div className="notice">
          当前后台使用的是前端门禁版登录，且仍保留默认账号配置。正式上线前请先修改
          <strong> `admin/config.js` </strong>
          中的管理员账号，并尽快把认证迁移到服务端或接入受保护网关。
        </div>
      ) : null}

      <section className="hero">
        <div className="hero-grid">
          <div>
            <h2>一眼看清当前 UR 的供应结构与同步健康度</h2>
            <div className="hero-copy">
              当前共跟踪 <strong>{compactNumber(derived.totalCatalogProperties)}</strong> 个物业目录，其中
              <strong> {compactNumber(derived.totalProperties)} </strong> 个物业存在空房，合计
              <strong> {compactNumber(derived.totalAvailableRooms)} </strong> 套可租房源。重点供应集中在
              <strong> {derived.largestRegion?.regionName || '暂无地区'} </strong>
              ，最近一次 refresh 状态为 <strong>{latestLabel}</strong>。
            </div>
          </div>

          <div className="hero-status">
            <div>
              <div className="hero-status-label">最后完成刷新</div>
              <div className="hero-status-value">{formatDateTime(derived.health?.lastRefreshFinishedAt)}</div>
            </div>
            <div>
              <div className="hero-status-label">下次计划刷新</div>
              <div className="hero-status-value">{formatDateTime(derived.health?.nextScheduledAt)}</div>
            </div>
            <div>
              <div className="hero-status-label">时间感知</div>
              <div className="hero-status-value">{formatRelative(derived.health?.nextScheduledAt)}</div>
            </div>
          </div>
        </div>
      </section>

      <section className="metrics-grid">
        <MetricCard
          label="空置物业数"
          value={compactNumber(derived.totalProperties)}
          copy={`在 ${compactNumber(derived.totalCatalogProperties)} 个目录物业中，当前有空房的物业数。`}
          pill={`覆盖率 ${formatPercent(derived.vacancyCoverage)}`}
        />
        <MetricCard
          label="当前空房套数"
          value={compactNumber(derived.totalAvailableRooms)}
          copy={`平均每个有空房物业约 ${compactNumber(derived.avgRoomsPerProperty)} 套。`}
          pill={`${compactNumber(derived.activeRegions.length)} 个地区有供应`}
        />
        <MetricCard
          label="活跃地区数"
          value={compactNumber(derived.activeRegions.length)}
          copy={`共覆盖 ${compactNumber(derived.totalRegions)} 个地区，其中 ${compactNumber(derived.silentRegions.length)} 个地区当前无空房。`}
          pill={`${compactNumber(derived.totalAreasWithVacancy)} 个分区有房`}
        />
        <MetricCard
          label="户型图缓存覆盖"
          value={formatPercent(derived.floorplanCoverage)}
          copy={`已缓存 ${compactNumber(derived.summary.totalCachedFloorplanImages || 0)} 张户型图。`}
          pill={`Top3 供应占比 ${formatPercent(derived.topThreeShare)}`}
        />
      </section>

      <section className="panels-grid">
        <div className="panel">
          <div className="panel-head">
            <div>
              <h3>地区供应排行</h3>
              <div className="panel-copy">按当前空房套数排序，快速看到供给最集中的地区。</div>
            </div>
            <span className="tag">{derived.topRegions.length} 个地区</span>
          </div>

          <div className="leaderboard">
            {derived.topRegions.map((item) => (
              <LeaderboardRow
                key={item.regionKey}
                name={item.regionName}
                sub={`${compactNumber(item.availablePropertyCount || 0)} 个物业`}
                value={item.availableRoomCount || 0}
                total={derived.totalAvailableRooms}
              />
            ))}
          </div>
        </div>

        <div className="panel">
          <div className="panel-head">
            <div>
              <h3>分区热点</h3>
              <div className="panel-copy">从 area 维度看哪几个细分区域当前最有供给。</div>
            </div>
            <span className="tag">Top {derived.topAreas.length}</span>
          </div>

          <div className="leaderboard">
            {derived.topAreas.map((item) => (
              <LeaderboardRow
                key={item.areaKey}
                name={`${item.regionName} · ${item.name}`}
                sub={`${compactNumber(item.availablePropertyCount || 0)} 个物业`}
                value={item.availableRoomCount || 0}
                total={derived.totalAvailableRooms}
              />
            ))}
          </div>
        </div>
      </section>

      <section className="panels-grid">
        <div className="panel">
          <div className="panel-head">
            <div>
              <h3>同步健康</h3>
              <div className="panel-copy">后台服务可用性、最近 refresh 结果和错误提示。</div>
            </div>
          </div>

          <div className="facts-grid">
            <FactCard
              title="最近状态"
              value={latestLabel}
              copy={`开始于 ${formatDateTime(derived.latest?.startedAt)}，更新于 ${formatDateTime(derived.latest?.updatedAt)}。`}
            />
            <FactCard
              title="自动刷新频率"
              value={`${compactNumber(derived.health?.refreshIntervalMinutes || 0)} 分钟`}
              copy={`同步窗口 ${derived.health?.syncTimezone || '未标注'} ${derived.health?.syncWindowTokyo ? `${derived.health.syncWindowTokyo.startHour}:00 - ${derived.health.syncWindowTokyo.endHour}:00` : ''}`}
            />
            <FactCard
              title="Refresh Logs"
              value={compactNumber(derived.health?.refreshLogCount || 0)}
              copy={`后台已记录 ${compactNumber(derived.health?.roomEventCount || 0)} 条 room event。`}
            />
            <FactCard
              title="最近错误"
              value={derived.health?.lastRefreshError ? '需要处理' : '无'}
              copy={derived.health?.lastRefreshError?.message || '最近未检测到 refresh 错误。'}
            />
          </div>
        </div>

        <div className="panel">
          <div className="panel-head">
            <div>
              <h3>值得关注的补充统计</h3>
              <div className="panel-copy">除了总量之外，更适合运营判断供给结构和集中度。</div>
            </div>
          </div>

          <div className="facts-grid">
            <FactCard
              title="最大供给地区"
              value={derived.largestRegion?.regionName || '暂无'}
              copy={`${compactNumber(derived.largestRegion?.availableRoomCount || 0)} 套空房，${compactNumber(derived.largestRegion?.availablePropertyCount || 0)} 个物业。`}
            />
            <FactCard
              title="无空房地区"
              value={compactNumber(derived.silentRegions.length)}
              copy={`${compactNumber(derived.totalRegions)} 个地区里，有 ${compactNumber(derived.silentRegions.length)} 个地区当前没有可租房源。`}
            />
            <FactCard
              title="Top3 集中度"
              value={formatPercent(derived.topThreeShare)}
              copy="前三大地区合计贡献的空房套数占比，可帮助判断供给是否过于集中。"
            />
            <FactCard
              title="目录详尽度"
              value={formatPercent(
                derived.summary.totalCatalogProperties
                  ? (derived.summary.totalCatalogPropertiesDetailed || 0) / derived.summary.totalCatalogProperties
                  : 0
              )}
              copy={`已补 detail 的目录数 ${compactNumber(derived.summary.totalCatalogPropertiesDetailed || 0)} / ${compactNumber(derived.summary.totalCatalogProperties || 0)}。`}
            />
          </div>

          <div style={{ marginTop: 16 }}>
            <div className="panel-copy" style={{ marginTop: 0, marginBottom: 10 }}>
              当前无空房的地区
            </div>
            <div className="tag-wrap">
              {derived.silentRegions.length ? derived.silentRegions.map((item) => (
                <span key={item.regionKey} className="tag">{item.regionName}</span>
              )) : <span className="tag">暂无</span>}
            </div>
          </div>
        </div>
      </section>
    </div>
  );
}

function LogsPage({ state, regions, onChangeFilters, onApplyFilters }) {
  const data = state.data || [];
  const summary = useMemo(() => {
    const changedRuns = data.filter((run) => run?.summary?.hasChanges);
    const successRuns = data.filter((run) => run?.status === 'success');
    const failedRuns = data.filter((run) => run?.status === 'failed');
    return {
      total: data.length,
      changed: changedRuns.length,
      success: successRuns.length,
      failed: failedRuns.length,
    };
  }, [data]);

  return (
    <div className="page-grid">
      <section className="toolbar">
        <div className="filter-grid">
          <div className="field">
            <label>地区</label>
            <select
              value={state.filters.regionKey}
              onChange={(event) => onChangeFilters({ ...state.filters, regionKey: event.target.value })}
            >
              <option value="">全部地区</option>
              {regions.map((region) => (
                <option key={region.regionKey} value={region.regionKey}>
                  {region.regionName}
                </option>
              ))}
            </select>
          </div>

          <div className="field">
            <label>触发方式</label>
            <select
              value={state.filters.trigger}
              onChange={(event) => onChangeFilters({ ...state.filters, trigger: event.target.value })}
            >
              <option value="">全部</option>
              <option value="scheduled">定时</option>
              <option value="manual">手动</option>
            </select>
          </div>

          <div className="field">
            <label>开始时间</label>
            <input
              type="datetime-local"
              value={state.filters.since}
              onChange={(event) => onChangeFilters({ ...state.filters, since: event.target.value })}
            />
          </div>

          <div className="field">
            <label>结束时间</label>
            <input
              type="datetime-local"
              value={state.filters.until}
              onChange={(event) => onChangeFilters({ ...state.filters, until: event.target.value })}
            />
          </div>

          <label className="checkbox-row">
            <input
              type="checkbox"
              checked={state.filters.changedOnly}
              onChange={(event) => onChangeFilters({ ...state.filters, changedOnly: event.target.checked })}
            />
            只看有变化
          </label>

          <div className="button-row">
            <button type="button" className="btn btn-primary" onClick={() => onApplyFilters(state.filters)} disabled={state.loading}>
              {state.loading ? <span className="spin">◌</span> : '应用筛选'}
            </button>
            <button
              type="button"
              className="btn btn-secondary"
              onClick={() => {
                const nextFilters = { regionKey: '', trigger: '', changedOnly: false, since: '', until: '' };
                onChangeFilters(nextFilters);
                onApplyFilters(nextFilters);
              }}
            >
              重置
            </button>
          </div>
        </div>
      </section>

      <section className="summary-grid">
        <SummaryCard title="日志条数" value={compactNumber(summary.total)} copy="当前筛选条件下返回的 refresh run 数量。" />
        <SummaryCard title="成功" value={compactNumber(summary.success)} copy="执行成功并返回 summary 的 refresh 次数。" />
        <SummaryCard title="失败" value={compactNumber(summary.failed)} copy="执行失败，通常值得优先排查错误栈。" />
        <SummaryCard title="有变化" value={compactNumber(summary.changed)} copy="这一轮存在新增或下架变化的 run 数量。" />
      </section>

      {state.error ? <div className="error">{state.error}</div> : null}

      {!state.loading && !data.length ? (
        <div className="empty">当前筛选条件下没有 refresh 日志。</div>
      ) : null}

      <section className="runs-list">
        {data.map((run) => (
          <RunCard key={run.runId} run={run} />
        ))}
      </section>
    </div>
  );
}

function RunCard({ run }) {
  const counts = summarizeChanges(run);
  const regions = run?.changes?.regions || [];
  const listedRooms = run?.changes?.rooms?.listed || [];
  const delistedRooms = run?.changes?.rooms?.delisted || [];
  const listedProperties = run?.changes?.properties?.listed || [];
  const delistedProperties = run?.changes?.properties?.delisted || [];

  return (
    <article className="run-card">
      <div className="run-head">
        <div>
          <div className="run-title">
            <span className={`badge badge-${toneForStatus(run.status)}`}>{labelForStatus(run.status)}</span>
            <span className="badge badge-neutral">{renderTrigger(run.trigger)}</span>
            <h3>{formatDateTime(run.startedAt)}</h3>
          </div>
          <div className="run-meta">
            <span>Run ID {run.runId}</span>
            <span>更新时间 {formatDateTime(run.updatedAt)}</span>
            <span>{run.summary?.hasChanges ? '本轮有变化' : '本轮无变化'}</span>
          </div>
        </div>
        <div className="tag-wrap">
          {regions.length ? regions.slice(0, 5).map((item) => (
            <span key={`${run.runId}:${item.regionKey || item}`} className="tag">
              {item.regionName || item.regionKey || item}
            </span>
          )) : <span className="tag">未标注地区</span>}
        </div>
      </div>

      <div className="run-grid">
        <RunStat label="新增空房" value={counts.listedRooms} />
        <RunStat label="下架空房" value={counts.delistedRooms} />
        <RunStat label="新增物业" value={counts.listedProperties} />
        <RunStat label="下架物业" value={counts.delistedProperties} />
        <RunStat label="空房净变化" value={counts.activeRoomDelta} />
      </div>

      {run.error?.message ? (
        <div className="run-error">
          <strong>错误信息：</strong> {run.error.message}
        </div>
      ) : null}

      <div className="changes-grid">
        <ChangeBlock title="新增空房" items={listedRooms} />
        <ChangeBlock title="下架空房" items={delistedRooms} />
        <ChangeBlock title="新增物业" items={listedProperties} />
        <ChangeBlock title="下架物业" items={delistedProperties} />
      </div>
    </article>
  );
}

function ChangeBlock({ title, items }) {
  return (
    <section className="change-card">
      <div className="change-title">{title}</div>
      {items.length ? (
        <div className="change-list">
          {items.slice(0, 4).map((item, index) => (
            <div key={`${title}:${index}:${entryLabel(item)}`} className="change-item">
              {entryLabel(item)}
            </div>
          ))}
          {items.length > 4 ? (
            <div className="change-empty">还有 {items.length - 4} 条未展开</div>
          ) : null}
        </div>
      ) : (
        <div className="change-empty">无记录</div>
      )}
    </section>
  );
}

function MetricCard({ label, value, copy, pill }) {
  return (
    <div className="metric-card">
      <div className="metric-label">{label}</div>
      <div className="metric-value">{value}</div>
      <div className="metric-copy">{copy}</div>
      <span className="metric-pill">{pill}</span>
    </div>
  );
}

function FactCard({ title, value, copy }) {
  return (
    <div className="fact-card">
      <div className="fact-title">{title}</div>
      <div className="fact-value">{value}</div>
      <div className="fact-copy">{copy}</div>
    </div>
  );
}

function LeaderboardRow({ name, sub, value, total }) {
  const ratio = total ? Math.max(6, ((value || 0) / total) * 100) : 0;
  return (
    <div className="leaderboard-row">
      <div className="leaderboard-meta">
        <div>
          <div className="leaderboard-name">{name}</div>
          <div className="leaderboard-sub">{sub}</div>
        </div>
        <div className="leaderboard-value">{compactNumber(value)} 套</div>
      </div>
      <div className="bar">
        <div className="bar-fill" style={{ width: `${Math.min(ratio, 100)}%` }}></div>
      </div>
    </div>
  );
}

function SummaryCard({ title, value, copy }) {
  return (
    <div className="summary-card">
      <div className="summary-title">{title}</div>
      <div className="summary-value">{value}</div>
      <div className="summary-copy">{copy}</div>
    </div>
  );
}

function RunStat({ label, value }) {
  const tone = value > 0 ? 'var(--accent-2)' : 'var(--ink)';
  return (
    <div className="run-stat">
      <div className="run-stat-label">{label}</div>
      <div className="run-stat-value" style={{ color: tone }}>
        {value > 0 ? `+${compactNumber(value)}` : compactNumber(value)}
      </div>
    </div>
  );
}

function LoginScreen({ onLogin }) {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState('');

  async function handleSubmit(event) {
    event.preventDefault();
    setSubmitting(true);
    setError('');

    try {
      const users = CONFIG.adminUsers || [];
      const matched = users.find((item) => item.username === username.trim());
      if (!matched) {
        throw new Error('用户名或密码不正确');
      }

      const passwordHash = await sha256(password);
      if (passwordHash !== matched.passwordHash) {
        throw new Error('用户名或密码不正确');
      }

      onLogin({
        username: matched.username,
        loggedAt: new Date().toISOString(),
      });
    } catch (nextError) {
      setError(nextError.message || '登录失败');
    } finally {
      setSubmitting(false);
    }
  }

  return (
    <div className="login-screen">
      <div className="login-card">
        <div className="login-side">
          <div className="brand-kicker">Private Console</div>
          <h1>UR Admin Console</h1>
          <p>
            独立于前台站点的后台入口，用于查看 refresh 审计日志、全局物业供应统计，以及当前同步健康状态。
          </p>

          <div className="login-facts">
            <div className="login-fact">
              <div className="login-fact-title">可查看</div>
              <div className="login-fact-value">Refresh Logs</div>
            </div>
            <div className="login-fact">
              <div className="login-fact-title">可查看</div>
              <div className="login-fact-value">物业 / 空房统计</div>
            </div>
            <div className="login-fact">
              <div className="login-fact-title">当前方式</div>
              <div className="login-fact-value">前端门禁登录</div>
            </div>
          </div>
        </div>

        <form className="login-form" onSubmit={handleSubmit}>
          <div>
            <h2>管理员登录</h2>
            <p>输入用户名和密码后进入后台。</p>
          </div>

          <div className="field">
            <label>用户名</label>
            <input value={username} onChange={(event) => setUsername(event.target.value)} placeholder="请输入用户名" />
          </div>

          <div className="field">
            <label>密码</label>
            <input
              type="password"
              value={password}
              onChange={(event) => setPassword(event.target.value)}
              placeholder="请输入密码"
            />
          </div>

          {getDefaultSetupActive() ? (
            <div className="login-hint">
              当前仍在使用默认账号配置。开发联调可先使用默认账号，正式部署前请修改
              <strong> `admin/config.js` </strong>。
            </div>
          ) : null}

          {error ? <div className="error">{error}</div> : null}

          <button type="submit" className="btn btn-primary" disabled={submitting}>
            {submitting ? <span className="spin">◌</span> : '登录后台'}
          </button>

          <div className="footer-note">
            说明：当前仓库是纯静态站，因此这里实现的是前端登录门禁。若要做到真正的安全隔离，建议把登录校验和 API token 一起迁移到服务端。
          </div>
        </form>
      </div>
    </div>
  );
}

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