import { useEffect, useState, type FormEvent } from "react";
import { Link, NavLink, useLocation, useNavigate } from "react-router-dom";
import { ApiError, api } from "../api";
import { AppShell } from "../components/AppShell";
import { EmptyState, FeedbackBanner, FreshnessBanner, LoadingState } from "../components/Feedback";
import { StatusBadge } from "../components/StatusBadge";
import { usePollingResource } from "../hooks/usePollingResource";
import { formatDateTime, formatTicketNumberRange, isTimestampStale } from "../lib/format";
import type { AdminOverviewDto, AdminProjectDto, AdminUserDto, CallBatchDto, CallMode } from "../types";
import { forecastRows } from "./DisplayPage";

function currentBatchLabel(value: AdminProjectDto["current_batch"]): string {
  if (value === null || value === undefined) return "暂无";
  if (typeof value === "string" || typeof value === "number") return String(value);
  const batch = value as CallBatchDto;
  return formatTicketNumberRange(batch.tickets.map((ticket) => ticket.ticket_number));
}

function currentBatchNumbers(value: AdminProjectDto["current_batch"]): string {
  if (!value || typeof value === "string" || typeof value === "number") return currentBatchLabel(value);
  const batch = value as CallBatchDto;
  const numbers = batch.tickets.map((ticket) => ticket.ticket_number).filter(Boolean);
  return numbers.length ? formatTicketNumberRange(numbers) : currentBatchLabel(batch);
}

function deviceStatusLabel(value: unknown): string {
  if (!value) return "未配置";
  if (typeof value === "string") return value;
  if (typeof value === "object") {
    const record = value as Record<string, unknown>;
    return String(record.label ?? record.status ?? record.message ?? "已配置");
  }
  return String(value);
}

function AdminNavigation() {
  return (
    <aside className="admin-sidebar" aria-label="管理端导航">
      <nav className="admin-nav" aria-label="管理任务">
        <NavLink end to="/admin">运营概览</NavLink>
        <NavLink to="/admin/projects">项目管理</NavLink>
        <NavLink to="/admin/history">历史数据</NavLink>
        <NavLink to="/admin/accounts">账号管理</NavLink>
        <NavLink to="/admin/display">大屏中心</NavLink>
      </nav>
    </aside>
  );
}

function OperationsOverview({ data, refreshing, onRefresh }: { data: AdminOverviewDto; refreshing: boolean; onRefresh: () => void }) {
  const [query, setQuery] = useState("");
  const [statusFilter, setStatusFilter] = useState("ALL");
  const projects = data.projects.filter((project) => {
    const matchesQuery = project.name.toLowerCase().includes(query.trim().toLowerCase());
    return matchesQuery && (statusFilter === "ALL" || project.status === statusFilter);
  });
  const summary = data.summary;

  return (
    <div className="operations-dashboard">
      <div className="operations-dashboard__overview">
        <section className="summary-strip" aria-label="运营汇总">
          <article><span>运行项目</span><strong>{summary.running_projects}</strong><small>个</small></article>
          <article><span>总等待人数</span><strong>{summary.waiting_people_count ?? 0}</strong><small>人</small></article>
        </section>
      </div>
      <div className="operations-dashboard__body">
        <section className="panel queue-overview" aria-labelledby="admin-projects-title">
          <div className="queue-overview__heading">
            <div><span className="section-marker" aria-hidden="true" /><h2 id="admin-projects-title">当前排队</h2></div>
            <button className="button button--secondary button--small" onClick={onRefresh} disabled={refreshing}>{refreshing ? "正在更新" : "刷新数据"}</button>
          </div>
          <div className="queue-overview__filters">
            <label><span className="sr-only">队列状态</span><select value={statusFilter} onChange={(event) => setStatusFilter(event.target.value)}><option value="ALL">全部状态</option><option value="RUNNING">运行中</option><option value="PAUSED">已暂停</option><option value="ENDED">已结束</option></select></label>
            <label className="queue-search"><span className="sr-only">搜索项目</span><input value={query} onChange={(event) => setQuery(event.target.value)} placeholder="搜索项目名称" /></label>
          </div>
          {projects.length ? (
            <>
              <div className="queue-project-list" role="list" aria-label="项目队列">
                <div className="queue-project-list__header" aria-hidden="true"><span>项目</span><span>等待人数</span><span>当前叫号</span><span>队列状态</span><span>更新时间</span></div>
                {projects.map((project) => (
                  <div className="queue-project-row" key={project.id} role="listitem">
                    <span className="queue-project-row__name"><strong>{project.name}</strong><small>{deviceStatusLabel(project.device_status)}</small></span>
                    <span className="queue-project-row__metric"><strong>{project.waiting_people_count ?? 0}</strong><small>人 · {project.waiting_ticket_count ?? project.waiting_count ?? 0} 个号码</small></span>
                    <span><strong>{currentBatchLabel(project.current_batch)}</strong></span>
                    <StatusBadge status={project.status} kind="project" />
                    <time>{formatDateTime(project.last_updated_at)}</time>
                  </div>
                ))}
              </div>
              <footer className="queue-overview__footer"><span>共 {projects.length} 项</span><span aria-label="当前第 1 页">上一页　<strong>1</strong>　下一页</span></footer>
            </>
          ) : <EmptyState title="没有符合条件的项目" />}
        </section>
      </div>
    </div>
  );
}

const DEFAULT_PROJECT_TIMEZONE = "Asia/Shanghai";
const DEFAULT_TICKET_PREFIX = "A";
const DEFAULT_VISITOR_NOTICE = "请您在景区附近等候，注意听从工作人员指引。";

type ProjectDraft = {
  name: string;
  code: string;
  status: string;
  callMode: CallMode;
  defaultCallTicketCount: string;
  maxCallTicketCount: string;
  defaultCallPeopleCount: string;
  maxCallPeopleCount: string;
  minPartySize: string;
  maxPartySize: string;
  gracePeriodMinutes: string;
  experiencedPeopleStart: string;
  etaIntervalSeconds: string;
  visitorNotice: string;
};

function draftFor(project?: AdminProjectDto): ProjectDraft {
  return {
    name: project?.name ?? "",
    code: project?.code ?? "",
    status: project?.status ?? "NOT_OPEN",
    callMode: project?.call_mode ?? "BOTH",
    defaultCallTicketCount: String(project?.default_call_ticket_count ?? project?.call_batch_size ?? project?.batch_size ?? 1),
    maxCallTicketCount: String(project?.max_call_ticket_count ?? 100),
    defaultCallPeopleCount: String(project?.default_call_people_count ?? 1),
    maxCallPeopleCount: String(project?.max_call_people_count ?? 100),
    minPartySize: String(project?.min_party_size ?? 1),
    maxPartySize: String(project?.max_party_size ?? 10),
    gracePeriodMinutes: String(project?.grace_period_minutes ?? 5),
    experiencedPeopleStart: String(project?.experienced_people_start ?? 0),
    etaIntervalSeconds: String(project?.eta?.interval_per_person_seconds ?? 60),
    visitorNotice: project ? project.visitor_notice ?? "" : DEFAULT_VISITOR_NOTICE,
  };
}

function ProjectForm({ project, onSaved }: { project?: AdminProjectDto; onSaved: () => void | Promise<void> }) {
  const navigate = useNavigate();
  const [draft, setDraft] = useState(() => draftFor(project));
  const [saving, setSaving] = useState(false);
  const [notice, setNotice] = useState<{ tone: "success" | "danger"; message?: string } | null>(null);

  const update = (patch: Partial<ProjectDraft>) => {
    setDraft((current) => ({ ...current, ...patch }));
    setNotice(null);
  };

  const submit = async (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    setSaving(true);
    setNotice(null);
    try {
      const profile = {
        name: draft.name,
        code: draft.code.toUpperCase(),
        timezone: project?.timezone ?? DEFAULT_PROJECT_TIMEZONE,
        ticket_prefix: project?.ticket_prefix ?? DEFAULT_TICKET_PREFIX,
      };
      let projectId = project?.id;
      if (projectId) {
        await api.updateProject(projectId, profile);
      } else {
        const response = await api.createProject(profile);
        projectId = response.project.id;
      }
      if (!projectId) throw new Error("项目创建未返回项目编号。");
      await api.updateProjectSettings(projectId, {
        status: draft.status,
        call_mode: draft.callMode,
        default_call_ticket_count: Number(draft.defaultCallTicketCount),
        max_call_ticket_count: Number(draft.maxCallTicketCount),
        default_call_people_count: Number(draft.defaultCallPeopleCount),
        max_call_people_count: Number(draft.maxCallPeopleCount),
        min_party_size: Number(draft.minPartySize),
        max_party_size: Number(draft.maxPartySize),
        grace_period_minutes: Number(draft.gracePeriodMinutes),
        experienced_people_start: Number(draft.experiencedPeopleStart),
        eta_interval_seconds: Number(draft.etaIntervalSeconds),
        visitor_notice: draft.visitorNotice,
      });
      await onSaved();
      if (project) {
        setNotice({ tone: "success", message: "项目维护内容已更新。" });
      } else {
        navigate("/admin/projects", { replace: true });
      }
    } catch (caught) {
      setNotice({ tone: "danger", message: caught instanceof ApiError ? caught.message : "项目保存失败，请检查后重试。" });
    } finally {
      setSaving(false);
    }
  };

  return (
    <form className="panel project-settings-form" aria-label={project ? "维护项目" : "创建项目"} onSubmit={submit}>
      {!project ? <div className="panel__header"><div><h2>创建项目</h2><p>创建项目并同时设置运行规则。</p></div></div> : null}
      <div className="project-form-sections">
        <section className="project-form-section" aria-labelledby="project-form-basics-title">
          <header className="project-form-section__header">
            <h3 id="project-form-basics-title">基础信息</h3>
            <p>用于识别项目和设置当前运行状态。</p>
          </header>
          <div className="project-form-section__grid">
            <label className="field">
              <span>项目名称</span>
              <input autoFocus={!project} value={draft.name} onChange={(event) => update({ name: event.target.value })} maxLength={120} disabled={saving} required />
            </label>
            <label className="field">
              <span>项目编码</span>
              <input value={draft.code} onChange={(event) => update({ code: event.target.value.toUpperCase() })} pattern="[A-Z0-9][A-Z0-9_-]{1,23}" disabled={saving} required />
            </label>
            <label className="field">
              <span>票号格式</span>
              <select value="00000" disabled><option value="00000">00000</option></select>
            </label>
            <label className="field">
              <span>项目状态</span>
              <select value={draft.status} onChange={(event) => update({ status: event.target.value })} disabled={saving}>
                <option value="NOT_OPEN">未开放</option>
                <option value="RUNNING">运行中</option>
                <option value="PAUSED">已暂停</option>
                <option value="ENDED">已结束</option>
              </select>
            </label>
          </div>
        </section>

        <section className="project-form-section" aria-labelledby="project-form-call-rules-title">
          <header className="project-form-section__header">
            <h3 id="project-form-call-rules-title">叫号规则</h3>
            <p>设置取号人数范围和员工端单次叫号上限。</p>
          </header>
          <div className="project-call-rules">
            <div className="project-call-rule" role="group" aria-labelledby="project-call-mode-title">
              <div className="project-call-rule__heading">
                <h4 id="project-call-mode-title">支持方式</h4>
                <p>决定员工端可使用的叫号方式。</p>
              </div>
              <div className="project-call-rule__fields project-call-rule__fields--single">
                <label className="field">
                  <span>叫号方式</span>
                  <select aria-label="支持的叫号方式" value={draft.callMode} onChange={(event) => update({ callMode: event.target.value as CallMode })} disabled={saving}>
                    <option value="TICKET">仅按号码</option>
                    <option value="PEOPLE">仅按人数</option>
                    <option value="BOTH">两种都支持</option>
                  </select>
                </label>
              </div>
            </div>

            <div className="project-call-rule" role="group" aria-labelledby="project-party-size-title">
              <div className="project-call-rule__heading">
                <h4 id="project-party-size-title">单号人数</h4>
                <p>限定每张号码可登记的人数范围。</p>
              </div>
              <div className="project-call-rule__fields">
                <label className="field">
                  <span>最少人数</span>
                  <input aria-label="单号最少人数" type="number" min="1" max={draft.maxPartySize || "10000"} value={draft.minPartySize} onChange={(event) => update({ minPartySize: event.target.value })} disabled={saving} required />
                </label>
                <label className="field">
                  <span>最多人数</span>
                  <input aria-label="单号最多人数" type="number" min={draft.minPartySize || "1"} max="10000" value={draft.maxPartySize} onChange={(event) => update({ maxPartySize: event.target.value })} disabled={saving} required />
                </label>
              </div>
            </div>

            <div className="project-call-rule" role="group" aria-labelledby="project-ticket-call-title">
              <div className="project-call-rule__heading">
                <h4 id="project-ticket-call-title">批量叫号</h4>
                <p>按号码个数批量叫号。</p>
              </div>
              <div className="project-call-rule__fields">
                <label className="field">
                  <span>默认数量</span>
                  <input aria-label="批量叫号默认数量" type="number" min="1" max={draft.maxCallTicketCount || "10000"} value={draft.defaultCallTicketCount} onChange={(event) => update({ defaultCallTicketCount: event.target.value })} disabled={saving} required />
                </label>
                <label className="field">
                  <span>单次上限</span>
                  <input aria-label="批量叫号单次上限" type="number" min={draft.defaultCallTicketCount || "1"} max="10000" value={draft.maxCallTicketCount} onChange={(event) => update({ maxCallTicketCount: event.target.value })} disabled={saving} required />
                  <small>限制号码个数，不限制合计人数。</small>
                </label>
              </div>
            </div>

            <div className="project-call-rule" role="group" aria-labelledby="project-people-call-title">
              <div className="project-call-rule__heading">
                <h4 id="project-people-call-title">批量叫人</h4>
                <p>按队列实际人数批量叫号。</p>
              </div>
              <div className="project-call-rule__fields">
                <label className="field">
                  <span>默认人数</span>
                  <input aria-label="批量叫人默认人数" type="number" min="1" max={draft.maxCallPeopleCount || "10000"} value={draft.defaultCallPeopleCount} onChange={(event) => update({ defaultCallPeopleCount: event.target.value })} disabled={saving} required />
                </label>
                <label className="field">
                  <span>单次上限</span>
                  <input aria-label="批量叫人单次上限" type="number" min={draft.defaultCallPeopleCount || "1"} max="10000" value={draft.maxCallPeopleCount} onChange={(event) => update({ maxCallPeopleCount: event.target.value })} disabled={saving} required />
                  <small>用于防止输入过大的叫人数量。</small>
                </label>
              </div>
            </div>
          </div>
        </section>

        <section className="project-form-section" aria-labelledby="project-form-other-rules-title">
          <header className="project-form-section__header">
            <h3 id="project-form-other-rules-title">其他规则</h3>
            <p>管理游客端展示数据、预计时间和官方提示。</p>
          </header>
          <div className="project-form-section__grid">
            <label className="field">
              <span>已体验人数起始展示数</span>
              <input aria-label="已体验人数起始展示数" type="number" min="0" max="1000000000" value={draft.experiencedPeopleStart} onChange={(event) => update({ experiencedPeopleStart: event.target.value })} disabled={saving} required />
              <small>每天开始时先显示此数；实际取号人数超过后显示实际人数。</small>
            </label>
            <label className="field">
              <span>每人预计间隔时间（秒）</span>
              <input aria-label="每人预计间隔时间（秒）" type="number" min="1" max="86400" value={draft.etaIntervalSeconds} onChange={(event) => update({ etaIntervalSeconds: event.target.value })} disabled={saving} required />
              <small>按本号前方实际人数累加计算预计等待时间。</small>
            </label>
            <label className="field project-settings-form__visitor-notice">
              <span>游客官方提示</span>
              <textarea aria-label="游客官方提示" value={draft.visitorNotice} onChange={(event) => update({ visitorNotice: event.target.value })} maxLength={240} rows={3} disabled={saving} placeholder="例如：请在入口附近等候，叫号后凭号码入场。" />
              <small>显示在游客页“刷新状态”按钮上方，最多 240 个字；留空则不显示。</small>
            </label>
          </div>
        </section>
      </div>
      <div className="project-settings-form__actions">
        {notice ? <FeedbackBanner tone={notice.tone} title={notice.tone === "success" ? "项目保存成功" : "保存失败"}>{notice.message || null}</FeedbackBanner> : null}
        {!project ? <Link className="button button--secondary" to="/admin/projects">取消</Link> : null}
        <button className="button button--primary" type="submit" disabled={saving}>{saving ? "正在保存" : project ? "保存项目" : "创建项目"}</button>
      </div>
    </form>
  );
}

function ProjectManagement({ projects }: { projects: AdminProjectDto[] }) {
  return <section className="panel">
    <div className="panel__header"><div><h2>项目管理</h2><p>创建项目，并在项目维护中管理基础信息与运行规则。</p></div><Link className="button button--primary" to="/admin/projects/new">创建项目</Link></div>
    {projects.length ? <div className="project-list" role="table" aria-label="项目列表">
      <div className="project-list__row project-list__header" role="row"><span role="columnheader">项目名称</span><span role="columnheader">项目编码</span><span role="columnheader">票号格式</span><span role="columnheader">状态</span><span role="columnheader">操作</span></div>
      {projects.map((project) => <div className="project-list__row" role="row" key={project.id}><strong role="cell">{project.name}</strong><span role="cell">{project.code}</span><span role="cell">00000</span><span role="cell"><StatusBadge status={project.status} kind="project" /></span><span className="project-list__actions" role="cell"><Link className="button button--secondary button--small" to={`/admin/projects/${project.id}`}>维护</Link></span></div>)}
    </div> : <EmptyState title="暂无项目" />}
  </section>;
}

function ProjectMaintenance({ project, onRefresh }: { project?: AdminProjectDto; onRefresh: () => void | Promise<void> }) {
  if (!project) return <section className="panel"><EmptyState title="未找到该项目" /><div className="account-maintenance__back"><Link className="button button--secondary" to="/admin/projects">返回项目列表</Link></div></section>;
  return <div className="project-maintenance">
    <div className="panel__header project-maintenance__header"><h2>{project.name}</h2><Link className="button button--secondary" to="/admin/projects">返回列表</Link></div>
    <ProjectForm project={project} onSaved={onRefresh} />
  </div>;
}

export function DisplayCenter({ projects }: { projects: AdminProjectDto[] }) {
  const [fullscreenError, setFullscreenError] = useState(false);
  const enterFullscreen = async (projectId: string) => {
    const tile = document.querySelector(`[data-project-screen="${projectId}"]`);
    if (!(tile instanceof HTMLElement) || !tile.requestFullscreen) {
      setFullscreenError(true);
      return;
    }
    try {
      await tile.requestFullscreen();
      setFullscreenError(false);
    } catch {
      setFullscreenError(true);
    }
  };

  return (
    <>
      {fullscreenError ? <FeedbackBanner tone="warning" title="浏览器未允许全屏">可继续在当前页面查看实时状态。</FeedbackBanner> : null}
      {projects.length ? (
        <section className="screen-wall" aria-label="项目实时监控墙">
          {projects.map((project) => <ProjectScreenTile project={project} onFullscreen={enterFullscreen} key={project.id} />)}
        </section>
      ) : <EmptyState title="暂无可监控项目" />}
    </>
  );
}

function ProjectScreenTile({ project, onFullscreen }: { project: AdminProjectDto; onFullscreen: (projectId: string) => Promise<void> }) {
  const current = project.current_batch && typeof project.current_batch === "object" ? project.current_batch as CallBatchDto : null;
  const waitingTicketCount = project.waiting_ticket_count ?? project.waiting_count ?? 0;
  const waitingPeopleCount = project.waiting_people_count ?? 0;
  const forecasts = forecastRows(current, waitingTicketCount, project.estimated_wait);
  const pageCount = Math.max(1, Math.ceil(forecasts.length / 4));
  const [page, setPage] = useState(0);
  const [now, setNow] = useState(() => new Date());
  useEffect(() => {
    setPage((value) => Math.min(value, pageCount - 1));
    if (pageCount <= 1) return undefined;
    const timer = window.setInterval(() => setPage((value) => (value + 1) % pageCount), 6_000);
    return () => window.clearInterval(timer);
  }, [pageCount]);
  useEffect(() => {
    const timer = window.setInterval(() => setNow(new Date()), 1_000);
    return () => window.clearInterval(timer);
  }, []);
  const visible = forecasts.slice(page * 4, (page + 1) * 4);
  const latestTicketNumber = project.latest_ticket_number ?? forecasts.at(-1)?.range.split("–").at(-1) ?? "暂无";
  const issuedTicketCount = project.issued_ticket_count ?? Number(latestTicketNumber.match(/\d+$/)?.[0] ?? 0);
  const experiencedPeople = project.experienced_people ?? project.experienced_people_start ?? 0;
  const dateLabel = new Intl.DateTimeFormat("zh-CN", { year: "numeric", month: "long", day: "numeric", weekday: "long" }).format(now);
  const timeLabel = new Intl.DateTimeFormat("zh-CN", { hour: "2-digit", minute: "2-digit", second: "2-digit", hour12: false }).format(now);

  return (
            <article className="screen-tile" data-project-screen={project.id}>
              <header className="screen-tile__header">
                <div className="screen-tile__clock" aria-label={`当前时间 ${dateLabel} ${timeLabel}`}>
                  <img className="screen-tile__logo" src="/xiaoqikong-logo.jpg" alt="" aria-hidden="true" />
                  <div className="screen-tile__clock-copy">
                    <time dateTime={now.toISOString()}>{timeLabel}</time>
                    <span>{dateLabel}</span>
                  </div>
                </div>
                <div className="screen-tile__project">
                  <h2>{project.name}</h2>
                </div>
                <button className="button button--primary screen-tile__fullscreen" onClick={() => void onFullscreen(project.id)}>全屏展示</button>
              </header>
              <div className="screen-tile__current">
                <strong>{currentBatchNumbers(project.current_batch)}</strong>
                <small>{project.current_batch ? "当前叫号" : "等待下一次叫号"}</small>
                <div className="screen-tile__current-metrics">
                  <h3 className="screen-tile__metric" aria-label={`最新取号码：${latestTicketNumber}${latestTicketNumber === "暂无" ? "" : "号"}`}><span>最新取号码：</span><strong>{latestTicketNumber}</strong>{latestTicketNumber === "暂无" ? null : <small>号</small>}</h3>
                  <div className="screen-tile__metric" aria-label={`累计取号数：${issuedTicketCount} 个`}><span>累计取号数：</span><strong>{issuedTicketCount}</strong><small>个</small></div>
                  <div className="screen-tile__metric" aria-label={`累计等待人数：${waitingPeopleCount} 人`}><span>累计等待人数：</span><strong>{waitingPeopleCount}</strong><small>人</small></div>
                  <div className="screen-tile__metric" aria-label={`已体验人数：${experiencedPeople} 人`}><span>已体验人数：</span><strong>{experiencedPeople}</strong><small>人</small></div>
                </div>
              </div>
              <section className="screen-tile__forecast" aria-label={`${project.name}最新取号信息`}>
                <div className="screen-tile__forecast-list-heading" aria-hidden="true"><span>后续号段</span><span>预计等待时间</span></div>
                {visible.length ? <ol>{visible.map((item) => <li key={item.range}><strong>{item.range}</strong><span>{item.wait}</span></li>)}</ol> : <p>后续区间暂不可估算</p>}
              </section>
            </article>
  );
}

function AccountManagement({ projects }: { projects: AdminProjectDto[] }) {
  const resource = usePollingResource((signal) => api.adminUsers(signal), { intervalMs: 30_000 });
  return <>
    <section className="panel"><div className="panel__header"><div><h2>账号管理</h2><p>查看账号并维护角色、状态与所属项目。</p></div><Link className="button button--primary" to="/admin/accounts/new">创建账号</Link></div>
      {resource.loading ? <LoadingState label="正在读取账号" /> : null}
      {resource.data?.users.length === 0 ? <EmptyState title="暂无账号" /> : <div className="account-list" role="table" aria-label="账号列表">
        <div className="account-list__row account-list__header" role="row"><span role="columnheader">账号</span><span role="columnheader">角色</span><span role="columnheader">状态</span><span role="columnheader">所属项目</span><span role="columnheader">操作</span></div>
        {resource.data?.users.map((user) => {
          const projectNames = projects.filter((project) => user.project_ids.includes(project.id)).map((project) => project.name);
          return <div className="account-list__row" role="row" key={user.id}><strong role="cell">{user.username}</strong><span role="cell">{user.role === "ADMIN" ? (user.protected ? "超级管理员" : "管理员") : "员工"}</span><span role="cell"><span className={`account-state account-state--${user.active ? "active" : "inactive"}`}>{user.active ? "已启用" : "已停用"}</span></span><span role="cell">{projectNames.length ? projectNames.join("、") : "未分配"}</span><span role="cell">{user.protected ? <span className="account-state">不可编辑</span> : <Link className="button button--secondary button--small" to={`/admin/accounts/${user.id}`}>维护</Link>}</span></div>;
        })}
      </div>}
    </section>
  </>;
}

function EditAccount({ projects, accountId }: { projects: AdminProjectDto[]; accountId: string }) {
  const resource = usePollingResource((signal) => api.adminUsers(signal), { intervalMs: 30_000 });
  const [error, setError] = useState<string | null>(null);
  const [notice, setNotice] = useState<string | null>(null);
  const [saving, setSaving] = useState(false);
  const [draft, setDraft] = useState<{ role: string; active: boolean; project_ids: string[] }>({ role: "STAFF", active: true, project_ids: [] });
  const user = resource.data?.users.find((item) => item.id === accountId);
  const toggleProject = (ids: string[], id: string) => ids.includes(id) ? ids.filter((value) => value !== id) : [...ids, id];
  useEffect(() => {
    if (user) setDraft({ role: user.role, active: user.active, project_ids: [...user.project_ids] });
  }, [user?.id]);
  const dirty = user ? (draft.role !== user.role || draft.active !== user.active || [...draft.project_ids].sort().join("|") !== [...user.project_ids].sort().join("|")) : false;
  const resetDraft = () => {
    if (!user) return;
    setDraft({ role: user.role, active: user.active, project_ids: [...user.project_ids] });
    setError(null);
    setNotice(null);
  };
  const save = async (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    if (!user || !dirty) return;
    setError(null);
    setNotice(null);
    setSaving(true);
    try {
      await api.updateAdminUser(user.id, draft);
      await resource.refresh();
      setNotice("账号修改已保存。");
    } catch (caught) { setError(caught instanceof ApiError ? caught.message : "账号保存失败"); }
    finally { setSaving(false); }
  };
  if (resource.loading && !resource.data) return <LoadingState label="正在读取账号" />;
  if (!user) return <section className="panel"><EmptyState title="未找到该账号" /><div className="account-maintenance__back"><Link className="button button--secondary" to="/admin/accounts">返回账号列表</Link></div></section>;
  if (user.protected) return <section className="panel"><EmptyState title="超级管理员账号不可编辑" /><div className="account-maintenance__back"><Link className="button button--secondary" to="/admin/accounts">返回账号列表</Link></div></section>;
  return <section className="panel account-maintenance">
    <div className="panel__header"><div><h2>维护账号</h2><p>账号：<strong>{user.username}</strong></p></div><Link className="button button--secondary" to="/admin/accounts">返回列表</Link></div>
    {error ? <FeedbackBanner tone="danger" title={error} /> : null}
    <form className="form-stack account-maintenance__form" aria-label="维护账号" onSubmit={save}>
      <div className="account-maintenance__basics">
        <div className="account-setting-card"><div className="account-setting-card__heading"><strong>角色权限</strong><small>选择该账号在系统中的身份</small></div><div className="account-role-options" role="radiogroup" aria-label="角色">{([['STAFF', '员工'], ['ADMIN', '管理员']] as const).map(([value, label]) => <label key={value} className="account-role-option"><input type="radio" name="account-role" value={value} checked={draft.role === value} onChange={() => setDraft((current) => ({ ...current, role: value }))} disabled={saving} /><span>{label}</span></label>)}</div></div>
        <label className="account-toggle"><span><strong>账号状态</strong><small>关闭后该账号将无法登录系统</small></span><input type="checkbox" checked={draft.active} onChange={(event) => setDraft((current) => ({ ...current, active: event.target.checked }))} disabled={saving} /><i aria-hidden="true" /></label>
      </div>
      <fieldset className="account-projects">
        <legend>所属项目</legend>
        <p>选择该账号可访问和操作的项目</p>
        <div className="account-projects__grid">
          {projects.map((project) => {
            const selected = draft.project_ids.includes(project.id);
            return <label className={`account-project-option${selected ? " account-project-option--selected" : ""}`} key={project.id}><input type="checkbox" checked={selected} onChange={() => setDraft((current) => ({ ...current, project_ids: toggleProject(current.project_ids, project.id) }))} disabled={saving} /><span>{project.name}</span><small>{selected ? "已授权" : "未授权"}</small></label>;
          })}
        </div>
      </fieldset>
      <div className="account-maintenance__actions">
        <div className="account-maintenance__feedback" aria-live="polite">{error ? <span className="account-save-state account-save-state--error">{error}</span> : notice ? <span className="account-save-state account-save-state--success">{notice}</span> : dirty ? <span className="account-save-state">有未保存的修改</span> : <span className="account-save-state">当前内容已保存</span>}</div>
        <button className="button button--secondary" type="button" onClick={resetDraft} disabled={!dirty || saving}>放弃修改</button>
        <button className="button button--primary" type="submit" disabled={!dirty || saving}>{saving ? "正在保存" : "保存修改"}</button>
      </div>
    </form>
  </section>;
}

function CreateAccount() {
  const navigate = useNavigate();
  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const create = async (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    setError(null);
    setSaving(true);
    try {
      await api.createAdminUser({ username, password, role: "STAFF", active: true, project_ids: [] });
      navigate("/admin/accounts", { replace: true });
    } catch (caught) {
      setError(caught instanceof ApiError ? caught.message : "账号创建失败");
    } finally {
      setSaving(false);
    }
  };
  return <section className="panel account-create-panel">
    <div className="panel__header"><div><h2>创建员工账号</h2><p>创建后可在账号列表中继续维护角色和所属项目。</p></div></div>
    {error ? <FeedbackBanner tone="danger" title={error} /> : null}
    <form className="form-stack" aria-label="创建账号" onSubmit={create}>
      <div className="field-row"><label className="field"><span>账号</span><input autoFocus autoComplete="username" value={username} onChange={(event) => setUsername(event.target.value)} disabled={saving} required /></label><label className="field"><span>初始密码</span><input type="password" minLength={8} autoComplete="new-password" value={password} onChange={(event) => setPassword(event.target.value)} disabled={saving} required /></label></div>
      <div className="account-create-panel__actions"><Link className="button button--secondary" to="/admin/accounts">取消</Link><button className="button button--primary" type="submit" disabled={saving}>{saving ? "正在创建" : "创建账号"}</button></div>
    </form>
  </section>;
}

export function AdminPage() {
  const location = useLocation();
  const resource = usePollingResource((signal) => api.adminOverview(signal), { intervalMs: 5_000 });
  const data = resource.data;
  const stale = Boolean(data) && isTimestampStale(resource.lastClientSuccessAt, 30_000);
  const section = location.pathname.startsWith("/admin/projects") ? "projects" : location.pathname.startsWith("/admin/accounts") ? "accounts" : location.pathname.endsWith("/display") ? "display" : "overview";
  const resourceId = location.pathname.split("/").at(-1) ?? "";

  return (
    <AppShell variant="admin">
      <div className="admin-workbench">
        <AdminNavigation />
        <section className="admin-content" aria-label="管理端内容">
          {resource.loading && !data ? <LoadingState label="正在汇总项目状态" /> : null}
          {resource.error && !data ? (
            <FeedbackBanner tone="danger" title="管理数据暂时不可用" action={<button className="button button--secondary button--small" onClick={resource.refresh}>重试</button>}>
              {resource.error.message}
            </FeedbackBanner>
          ) : null}
          {data ? (
            <>
              <FreshnessBanner offline={resource.offline} stale={stale} timestamp={resource.lastClientSuccessAt} refreshing={resource.refreshing} errorMessage={resource.error?.message} onRetry={resource.refresh} />
              {section === "overview" ? <OperationsOverview data={data} refreshing={resource.refreshing} onRefresh={resource.refresh} /> : null}
              {section === "projects" ? (location.pathname.endsWith("/new") ? <ProjectForm onSaved={resource.refresh} /> : location.pathname === "/admin/projects" ? <ProjectManagement projects={data.projects} /> : <ProjectMaintenance project={data.projects.find((project) => project.id === resourceId)} onRefresh={resource.refresh} />) : null}
              {section === "accounts" ? (location.pathname.endsWith("/new") ? <CreateAccount /> : location.pathname === "/admin/accounts" ? <AccountManagement projects={data.projects} /> : <EditAccount projects={data.projects} accountId={location.pathname.split("/").at(-1) ?? ""} />) : null}
              {section === "display" ? <DisplayCenter projects={data.projects} /> : null}
            </>
          ) : null}
        </section>
      </div>
    </AppShell>
  );
}
