import {
  AlertTriangle,
  Boxes,
  CheckCircle2,
  Download,
  FileJson,
  Lock,
  LogOut,
  RefreshCw,
  Server,
  Shield,
  UserRound
} from "lucide-react";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { createRoot } from "react-dom/client";
import "./styles.css";

type ApiResult<T> = T & {
  ok?: boolean;
  error?: string;
  message?: string;
};

type User = {
  id: number;
  username: string;
  email?: string;
  is_admin: boolean;
  is_active: boolean;
};

type ReleaseFile = {
  name: string;
  size: number;
  size_text: string;
  mod_time: string;
  url: string;
  kind: string;
  sha256?: string;
};

type Manifest = {
  latestVersion?: string;
  channel?: string;
  createdAt?: string;
  published_at?: string;
  latest?: {
    version?: string;
    channel?: string;
    published_at?: string;
    fullInstaller?: ReleaseSummary;
    msix?: ReleaseSummary;
  };
  fullInstaller?: ReleaseSummary;
  msix?: ReleaseSummary;
  messages?: Record<string, string>;
};

type ReleaseSummary = {
  fileName?: string;
  url?: string;
  sha256?: string;
  size?: number;
  version?: string;
};

type SystemInfo = {
  users?: number;
  routes?: number;
  logs?: number;
  version?: string;
  server_time?: string;
};

type LogEntry = {
  time: string;
  level: string;
  message: string;
};

async function api<T>(path: string, init?: RequestInit): Promise<ApiResult<T>> {
  const response = await fetch(path, {
    credentials: "include",
    headers: {
      "Content-Type": "application/json",
      ...(init?.headers ?? {})
    },
    ...init
  });

  const text = await response.text();
  const data = text ? JSON.parse(text) : {};
  if (!response.ok) {
    throw Object.assign(new Error(data.message || data.error || response.statusText), {
      status: response.status,
      data
    });
  }
  return data;
}

function App() {
  const [user, setUser] = useState<User | null>(null);
  const [checking, setChecking] = useState(true);
  const [loginError, setLoginError] = useState("");

  const refreshSession = useCallback(async () => {
    setChecking(true);
    try {
      const result = await api<{ user: User }>("/api/admin/me");
      setUser(result.user);
    } catch {
      setUser(null);
    } finally {
      setChecking(false);
    }
  }, []);

  useEffect(() => {
    void refreshSession();
  }, [refreshSession]);

  async function handleLogin(username: string, password: string) {
    setLoginError("");
    try {
      const result = await api<{ user: User }>("/api/admin/login", {
        method: "POST",
        body: JSON.stringify({ username, password })
      });
      setUser(result.user);
    } catch (error) {
      setLoginError(error instanceof Error ? error.message : "登录失败");
    }
  }

  async function handleLogout() {
    await api("/api/admin/logout", { method: "POST" });
    setUser(null);
  }

  if (checking) {
    return (
      <div className="grid min-h-screen place-items-center p-6">
        <span className="loading loading-spinner loading-lg text-primary" />
      </div>
    );
  }

  if (!user) {
    return <UnauthorizedView error={loginError} onLogin={handleLogin} />;
  }

  return <AdminDashboard user={user} onLogout={handleLogout} />;
}

function UnauthorizedView({
  error,
  onLogin
}: {
  error: string;
  onLogin: (username: string, password: string) => Promise<void>;
}) {
  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");
  const [submitting, setSubmitting] = useState(false);

  async function submit(event: React.FormEvent) {
    event.preventDefault();
    setSubmitting(true);
    try {
      await onLogin(username, password);
    } finally {
      setSubmitting(false);
    }
  }

  return (
    <main className="grid min-h-screen place-items-center px-4 py-10">
      <section className="surface w-full max-w-5xl rounded-lg p-5 lg:p-7">
        <div className="grid gap-6 lg:grid-cols-[1.05fr_0.95fr] lg:items-center">
          <div className="space-y-5">
            <div className="flex h-12 w-12 items-center justify-center rounded-lg bg-warning/15 text-warning">
              <Lock size={24} />
            </div>
            <div>
              <p className="text-sm font-semibold uppercase tracking-wide text-warning">Unauthorized</p>
              <h1 className="mt-2 text-3xl font-semibold tracking-normal text-base-content">未授权 / 请登录</h1>
              <p className="mt-3 max-w-xl text-base leading-7 text-base-content/70">
                后台 API 仅允许已认证管理员访问。登录成功后可管理完整安装包、MSIX 发布物、清单刷新和会话状态。
              </p>
            </div>
            <div className="alert border-warning/20 bg-warning/10 text-warning">
              <AlertTriangle size={18} />
              <span>未登录访问后台时只显示此授权边界，不会提前加载后台数据。</span>
            </div>
          </div>

          <form className="rounded-lg border border-base-300 bg-base-100 p-5 shadow-sm" onSubmit={submit}>
            <div className="mb-5 flex items-center gap-3">
              <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-primary/10 text-primary">
                <Shield size={20} />
              </div>
              <div>
                <h2 className="text-lg font-semibold">管理员登录</h2>
                <p className="text-sm text-base-content/60">使用已有管理员账号继续</p>
              </div>
            </div>
            <label className="form-control">
              <span className="label-text">用户名</span>
              <input
                className="input input-bordered mt-1"
                value={username}
                autoComplete="username"
                onChange={(event) => setUsername(event.target.value)}
                required
              />
            </label>
            <label className="form-control mt-3">
              <span className="label-text">密码</span>
              <input
                className="input input-bordered mt-1"
                type="password"
                value={password}
                autoComplete="current-password"
                onChange={(event) => setPassword(event.target.value)}
                required
              />
            </label>
            {error && <div className="alert alert-error mt-4 py-2 text-sm">{error}</div>}
            <button className="btn btn-primary mt-5 w-full" disabled={submitting} type="submit">
              {submitting ? <span className="loading loading-spinner loading-sm" /> : <UserRound size={18} />}
              登录
            </button>
          </form>
        </div>
      </section>
    </main>
  );
}

function AdminDashboard({ user, onLogout }: { user: User; onLogout: () => Promise<void> }) {
  const [manifest, setManifest] = useState<Manifest | null>(null);
  const [files, setFiles] = useState<ReleaseFile[]>([]);
  const [system, setSystem] = useState<SystemInfo | null>(null);
  const [logs, setLogs] = useState<LogEntry[]>([]);
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState("");

  const latest = manifest?.latest;
  const fullInstaller = latest?.fullInstaller ?? manifest?.fullInstaller;
  const msix = latest?.msix ?? manifest?.msix;
  const latestVersion = manifest?.latestVersion ?? latest?.version ?? fullInstaller?.version ?? "unknown";

  const refresh = useCallback(async () => {
    setBusy(true);
    setError("");
    try {
      const [manifestResponse, fileResponse, systemResponse, logResponse] = await Promise.all([
        fetch("/update-info.json", { credentials: "include" }).then((response) => response.json()),
        api<{ files: ReleaseFile[] }>("/api/admin/releases/files"),
        api<SystemInfo>("/api/admin/system"),
        api<{ logs: LogEntry[] }>("/api/admin/logs?limit=8")
      ]);
      setManifest(manifestResponse);
      setFiles(fileResponse.files ?? []);
      setSystem(systemResponse);
      setLogs(logResponse.logs ?? []);
    } catch (loadError) {
      setError(loadError instanceof Error ? loadError.message : "加载失败");
    } finally {
      setBusy(false);
    }
  }, []);

  useEffect(() => {
    void refresh();
  }, [refresh]);

  const metrics = useMemo(
    () => [
      { label: "最新版本", value: latestVersion, icon: CheckCircle2 },
      { label: "发布文件", value: String(files.length), icon: Download },
      { label: "后台用户", value: String(system?.users ?? 0), icon: Shield },
      { label: "日志数量", value: String(system?.logs ?? 0), icon: Server }
    ],
    [files.length, latestVersion, system?.logs, system?.users]
  );

  return (
    <div className="app-shell">
      <aside className="admin-sidebar p-4">
        <div className="flex items-center gap-3">
          <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-primary text-primary-content">
            <Boxes size={20} />
          </div>
          <div>
            <h1 className="text-base font-semibold">YMhut Update</h1>
            <p className="text-xs text-base-content/60">Full installer / MSIX</p>
          </div>
        </div>

        <nav className="menu mt-6 rounded-lg bg-base-100 p-2">
          <li>
            <a className="active">
              <Server size={16} />
              发布概览
            </a>
          </li>
          <li>
            <a href="/update-info.json">
              <FileJson size={16} />
              清单 JSON
            </a>
          </li>
        </nav>

        <div className="mt-6 rounded-lg border border-base-300 bg-base-100 p-3">
          <p className="text-xs text-base-content/60">当前会话</p>
          <p className="mt-1 font-medium">{user.username}</p>
          <p className="text-xs text-base-content/60">{user.is_admin ? "Administrator" : "User"}</p>
          <button className="btn btn-ghost btn-sm mt-3 w-full justify-start" onClick={() => void onLogout()}>
            <LogOut size={16} />
            退出登录
          </button>
        </div>
      </aside>

      <main className="admin-main">
        <header className="mb-4 flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
          <div>
            <p className="text-sm font-semibold uppercase tracking-wide text-primary">Admin</p>
            <h2 className="text-2xl font-semibold tracking-normal">更新发布后台</h2>
          </div>
          <div className="join">
            <button className="btn join-item" onClick={() => void refresh()} disabled={busy}>
              {busy ? <span className="loading loading-spinner loading-sm" /> : <RefreshCw size={17} />}
              刷新
            </button>
            <a className="btn btn-primary join-item" href="/" target="_blank" rel="noreferrer">
              前台
            </a>
          </div>
        </header>

        {error && <div className="alert alert-error mb-4">{error}</div>}

        <section className="metric-grid mb-4">
          {metrics.map((metric) => (
            <article className="surface rounded-lg p-4" key={metric.label}>
              <metric.icon className="mb-3 text-primary" size={20} />
              <p className="text-sm text-base-content/60">{metric.label}</p>
              <p className="mt-1 truncate text-xl font-semibold">{metric.value}</p>
            </article>
          ))}
        </section>

        <section className="content-grid">
          <div className="space-y-4">
            <Panel title="发布物" subtitle="downloads 白名单安装产物">
              <div className="overflow-x-auto">
                <table className="table table-sm">
                  <thead>
                    <tr>
                      <th>文件</th>
                      <th>类型</th>
                      <th>大小</th>
                      <th>更新时间</th>
                      <th />
                    </tr>
                  </thead>
                  <tbody>
                    {files.map((file) => (
                      <tr key={file.name}>
                        <td className="max-w-[280px] truncate">{file.name}</td>
                        <td>
                          <span className="badge badge-outline rounded-md">{file.kind}</span>
                        </td>
                        <td>{file.size_text}</td>
                        <td>{file.mod_time}</td>
                        <td>
                          <a className="btn btn-ghost btn-xs" href={file.url}>
                            下载
                          </a>
                        </td>
                      </tr>
                    ))}
                    {files.length === 0 && (
                      <tr>
                        <td className="text-base-content/60" colSpan={5}>
                          暂无发布文件
                        </td>
                      </tr>
                    )}
                  </tbody>
                </table>
              </div>
            </Panel>

            <Panel title="清单摘要" subtitle="公开清单仅包含完整安装包和 MSIX">
              <div className="grid gap-3 md:grid-cols-2">
                <SummaryCard title="完整安装包" item={fullInstaller} />
                <SummaryCard title="MSIX" item={msix} />
              </div>
            </Panel>
          </div>

          <div className="space-y-4">
            <Panel title="服务状态" subtitle={system?.server_time ?? "loading"}>
              <dl className="grid grid-cols-2 gap-3 text-sm">
                <Info label="版本" value={system?.version ?? "-"} />
                <Info label="路由" value={String(system?.routes ?? 0)} />
                <Info label="用户" value={String(system?.users ?? 0)} />
                <Info label="日志" value={String(system?.logs ?? 0)} />
              </dl>
            </Panel>

            <Panel title="最近日志" subtitle="后台运行事件">
              <div className="space-y-2">
                {logs.map((log) => (
                  <div className="rounded-md bg-base-200/80 p-2 text-sm" key={`${log.time}-${log.message}`}>
                    <div className="flex items-center justify-between gap-2">
                      <span className="font-medium">{log.level}</span>
                      <span className="text-xs text-base-content/50">{log.time}</span>
                    </div>
                    <p className="mt-1 break-words text-base-content/70">{log.message}</p>
                  </div>
                ))}
                {logs.length === 0 && <p className="text-sm text-base-content/60">暂无日志</p>}
              </div>
            </Panel>
          </div>
        </section>
      </main>
    </div>
  );
}

function Panel({ title, subtitle, children }: { title: string; subtitle?: string; children: React.ReactNode }) {
  return (
    <section className="surface rounded-lg p-4">
      <div className="mb-4 flex items-start justify-between gap-3">
        <div>
          <h3 className="font-semibold">{title}</h3>
          {subtitle && <p className="text-sm text-base-content/60">{subtitle}</p>}
        </div>
      </div>
      {children}
    </section>
  );
}

function SummaryCard({ title, item }: { title: string; item?: ReleaseSummary }) {
  return (
    <article className="rounded-lg border border-base-300 bg-base-100 p-3">
      <p className="text-sm font-medium">{title}</p>
      <p className="mt-2 truncate text-sm text-base-content/70">{item?.fileName ?? "未配置"}</p>
      <p className="mono mt-2 truncate text-xs text-base-content/50">{item?.sha256 ?? "-"}</p>
    </article>
  );
}

function Info({ label, value }: { label: string; value: string }) {
  return (
    <div className="rounded-md bg-base-200/80 p-3">
      <dt className="text-base-content/55">{label}</dt>
      <dd className="mt-1 font-medium">{value}</dd>
    </div>
  );
}

createRoot(document.getElementById("root")!).render(<App />);
