'use client';

import { useState } from 'react';
import { RefreshCw, RotateCw, Trash2, Plus } from 'lucide-react';
import { toast } from 'sonner';
import {
  HostActionKey,
  HOST_ACTION_META,
  type HostDetail,
  type ActionUrlsConfig,
} from '@pac/types';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/components/ui/select';
import { isPostMessageSentinel } from '@/lib/host-message';
import { hostApi } from './host-api';
import { useConfirm } from './use-confirm';
import { SecretRevealDialog } from './secret-reveal-dialog';
import { ColdImportCard } from './cold-import-card';

/**
 * HostAdminApp — 宿主自己的 admin 配置页主视图。
 * 用 shadcn 组件:AlertDialog(confirm)+ Dialog(secret reveal)+ Sonner(toast)+ Select(下拉)。
 */
export function HostAdminApp({ data, onRefresh }: { data: HostDetail; onRefresh: () => void }) {
  const [busy, setBusy] = useState(false);
  const [revealedSecret, setRevealedSecret] = useState<{
    title: string;
    description: React.ReactNode;
    secret: string;
  } | null>(null);
  const { confirm, dialog: confirmDialog } = useConfirm();

  // ── 轮换 appSecret ──
  const onRotateAppSecret = async () => {
    if (
      !(await confirm({
        title: '轮换 App Secret?',
        description: (
          <>
            旧 secret 将<strong className="text-foreground"> 立即失效</strong>,所有用旧 secret 换票的请求会被拒绝。
            <br />
            新 secret 仅会显示一次。
          </>
        ),
        confirmText: '继续轮换',
        danger: true,
      }))
    )
      return;

    setBusy(true);
    try {
      const r = await hostApi.rotateAppSecret();
      setRevealedSecret({
        title: 'App Secret 已轮换',
        description: '把下面的明文 secret 给宿主工程师替换。',
        secret: r.appSecret,
      });
      onRefresh();
    } catch (e) {
      toast.error('轮换失败', { description: e instanceof Error ? e.message : String(e) });
    } finally {
      setBusy(false);
    }
  };

  const onAddPushSecret = async () => {
    if (
      !(await confirm({
        title: '添加新 Push Secret?',
        description: (
          <>
            新 secret 会前插到数组(成为 <code>current</code>),旧 secret 进入 grace period 仍接受。
            <br />
            等宿主切完再来"删除"旧 secret。
          </>
        ),
        confirmText: '生成新 secret',
      }))
    )
      return;

    setBusy(true);
    try {
      const r = await hostApi.addPushSecret();
      setRevealedSecret({
        title: `Push Secret 已添加(共 ${r.totalKeys} 条)`,
        description: '新 secret 是 current;旧的留作 grace,等宿主切换后删除。',
        secret: r.pushSecret,
      });
      onRefresh();
    } catch (e) {
      toast.error('添加失败', { description: e instanceof Error ? e.message : String(e) });
    } finally {
      setBusy(false);
    }
  };

  const onRemovePushSecret = async (suffix: string) => {
    if (
      !(await confirm({
        title: `删除 Push Secret ...${suffix}?`,
        description: '宿主侧若仍用此 secret 发送的请求会被拒签。确认前请确认宿主已切到新 key。',
        confirmText: '删除',
        danger: true,
      }))
    )
      return;
    setBusy(true);
    try {
      await hostApi.removePushSecret(suffix);
      toast.success('已删除', { description: `...${suffix}` });
      onRefresh();
    } catch (e) {
      toast.error('删除失败', { description: e instanceof Error ? e.message : String(e) });
    } finally {
      setBusy(false);
    }
  };

  const onAddCallbackSecret = async () => {
    if (
      !(await confirm({
        title: '添加新 Callback Secret?',
        description: (
          <>
            新 secret 前插为 <code>current</code>,PAC 之后用它给回执签名;旧 secret 进 grace period。
            <br />
            等宿主切完再来"删除"旧 secret。
          </>
        ),
        confirmText: '生成新 secret',
      }))
    )
      return;

    setBusy(true);
    try {
      const r = await hostApi.addCallbackSecret();
      setRevealedSecret({
        title: `Callback Secret 已添加(共 ${r.totalKeys} 条)`,
        description: '把下面的明文 secret 给宿主用于回执验签;旧的留作 grace,切换后删除。',
        secret: r.callbackSecret,
      });
      onRefresh();
    } catch (e) {
      toast.error('添加失败', { description: e instanceof Error ? e.message : String(e) });
    } finally {
      setBusy(false);
    }
  };

  const onRemoveCallbackSecret = async (suffix: string) => {
    if (
      !(await confirm({
        title: `删除 Callback Secret ...${suffix}?`,
        description: '宿主侧若仍用此 secret 验签,PAC 用新 key 签的回执会被拒。确认宿主已切换。',
        confirmText: '删除',
        danger: true,
      }))
    )
      return;
    setBusy(true);
    try {
      await hostApi.removeCallbackSecret(suffix);
      toast.success('已删除', { description: `...${suffix}` });
      onRefresh();
    } catch (e) {
      toast.error('删除失败', { description: e instanceof Error ? e.message : String(e) });
    } finally {
      setBusy(false);
    }
  };

  const onToggleActive = async () => {
    const goingActive = !data.active;
    const verb = goingActive ? '激活' : '停用';
    if (
      !(await confirm({
        title: `${verb}宿主 "${data.name}"?`,
        description: goingActive
          ? '激活后宿主可重新换票,push 接收恢复。'
          : (
            <>
              停用后宿主<strong className="text-foreground">所有换票直接 401</strong>,push 拒收,数据接入暂停。
              <br />
              已落库的 patient/plan 不动,客服工作台仍可消费现有数据。
            </>
          ),
        confirmText: verb,
        danger: !goingActive,
      }))
    )
      return;
    setBusy(true);
    try {
      await hostApi.update({ active: goingActive });
      toast.success(`${verb}成功`, { description: `host=${data.name} active=${goingActive}` });
      onRefresh();
    } catch (e) {
      toast.error(`${verb}失败`, { description: e instanceof Error ? e.message : String(e) });
    } finally {
      setBusy(false);
    }
  };

  return (
    <main className="container mx-auto max-w-5xl space-y-5 p-6">
      {/* TopBar */}
      <header className="flex items-start justify-between gap-4">
        <div>
          <div className="flex items-center gap-2">
            <h1 className="text-2xl font-semibold tracking-tight">{data.name}</h1>
            <Badge variant={data.active ? 'default' : 'outline'}>
              {data.active ? 'active' : 'inactive'}
            </Badge>
          </div>
          <div className="mt-1 text-xs text-muted-foreground font-mono">
            id: {data.id} · appId: {data.appId}
          </div>
          <div className="mt-0.5 text-xs text-muted-foreground">
            创建于 {data.createdAt.slice(0, 19).replace('T', ' ')}
            {' · '}更新于 {data.updatedAt.slice(0, 19).replace('T', ' ')}
          </div>
        </div>
        <div className="flex flex-none items-center gap-2">
          <Button variant="outline" size="sm" onClick={onRefresh} disabled={busy}>
            <RefreshCw className="h-4 w-4" /> 刷新
          </Button>
          <Button
            variant={data.active ? 'outline' : 'default'}
            size="sm"
            onClick={onToggleActive}
            disabled={busy}
          >
            {data.active ? '停用' : '激活'}
          </Button>
        </div>
      </header>

      <StatsCard data={data} />

      {/* 数据导入(上传压缩包触发 cold-import) */}
      <ColdImportCard onDone={onRefresh} />

      {/* 凭据 */}
      <Card>
        <CardHeader>
          <CardTitle>凭据(明文永不存储)</CardTitle>
          <CardDescription>轮换后新 secret 仅本次显示一次,关闭后无法找回</CardDescription>
        </CardHeader>
        <CardContent className="space-y-4">
          {/* appSecret */}
          <div className="surface-inset flex items-center justify-between p-3">
            <div>
              <div className="text-sm font-medium">App Secret</div>
              <div className="mt-1 text-xs text-muted-foreground font-mono">
                {data.appSecret.masked}
              </div>
              {data.appSecret.rotatedAt && (
                <div className="mt-0.5 text-[10.5px] text-muted-foreground">
                  上次更新 {data.appSecret.rotatedAt.slice(0, 19).replace('T', ' ')}
                </div>
              )}
            </div>
            <Button variant="outline" size="sm" onClick={onRotateAppSecret} disabled={busy}>
              <RotateCw className="h-4 w-4" /> Rotate
            </Button>
          </div>

          {/* push secrets */}
          <div className="surface-inset p-3">
            <div className="flex items-center justify-between">
              <div>
                <div className="text-sm font-medium">Push Secrets</div>
                <div className="mt-1 text-xs text-muted-foreground">
                  {data.pushSecrets.length === 0
                    ? 'push 通道未启用'
                    : `共 ${data.pushSecrets.length} 条,索引 0 = current,其余 grace`}
                </div>
              </div>
              <Button variant="outline" size="sm" onClick={onAddPushSecret} disabled={busy}>
                <Plus className="h-4 w-4" /> Add new
              </Button>
            </div>
            {data.pushSecrets.length > 0 && (
              <ul className="mt-3 space-y-1.5">
                {data.pushSecrets.map((s) => (
                  <li
                    key={s.suffix}
                    className="flex items-center justify-between rounded-md row-quiet px-3 py-1.5 text-xs"
                  >
                    <div className="font-mono">...{s.suffix}</div>
                    <div className="flex items-center gap-2">
                      <Badge variant={s.role === 'current' ? 'default' : 'outline'}>
                        {s.role}
                      </Badge>
                      <Button
                        variant="ghost"
                        size="sm"
                        disabled={busy || data.pushSecrets.length === 1}
                        onClick={() => onRemovePushSecret(s.suffix)}
                        title={data.pushSecrets.length === 1 ? '不能删除唯一一条' : '删除'}
                      >
                        <Trash2 className="h-3.5 w-3.5" />
                      </Button>
                    </div>
                  </li>
                ))}
              </ul>
            )}
          </div>

          {/* 回执:callbackUrl + callback secrets(执行结果回调,PAC → 宿主) */}
          <CallbackConfig
            data={data}
            busy={busy}
            onAdd={onAddCallbackSecret}
            onRemove={onRemoveCallbackSecret}
            onSavedUrl={() => {
              onRefresh();
              toast.success('回执地址已保存');
            }}
            onError={(msg) => toast.error('保存失败', { description: msg })}
          />
        </CardContent>
      </Card>


      {/* Action URLs */}
      <ActionUrlsCard
        urls={data.actionUrls}
        busy={busy}
        onSaved={() => {
          onRefresh();
          toast.success('Action URLs 已保存');
        }}
        onError={(msg) => toast.error('保存失败', { description: msg })}
      />

      {/* 最近事件 */}
      <Card>
        <CardHeader>
          <CardTitle>最近事件</CardTitle>
          <CardDescription>最近 10 条 sync 记录(pull / push / reconcile / cold_import)</CardDescription>
        </CardHeader>
        <CardContent>
          {data.stats.recentEvents.length === 0 ? (
            <div className="text-sm text-muted-foreground">暂无事件</div>
          ) : (
            <ul className="space-y-1.5 text-xs font-mono">
              {data.stats.recentEvents.map((e, i) => (
                <li key={i} className="flex items-center gap-3 rounded row-quiet px-2 py-1">
                  <span className="text-muted-foreground nums w-32">
                    {e.startedAt.slice(0, 19).replace('T', ' ')}
                  </span>
                  <span className="w-20">
                    <KindPill kind={e.kind} />
                  </span>
                  <span className="w-16">
                    <StatusPill status={e.status} />
                  </span>
                  <span>+{e.transactionsWritten} txn</span>
                  {e.errorMessage && (
                    <span className="text-rose-600 truncate flex-1">{e.errorMessage}</span>
                  )}
                </li>
              ))}
            </ul>
          )}
        </CardContent>
      </Card>

      {confirmDialog}
      {revealedSecret && (
        <SecretRevealDialog
          open={!!revealedSecret}
          title={revealedSecret.title}
          description={revealedSecret.description}
          secret={revealedSecret.secret}
          onClose={() => setRevealedSecret(null)}
        />
      )}
    </main>
  );
}

// ─────────────────────────────────────────────
// 子组件
// ─────────────────────────────────────────────

function StatsCard({ data }: { data: HostDetail }) {
  const s = data.stats;
  const items = [
    { label: '患者总数', value: s.patientCount.toLocaleString() },
    { label: '24h 交易', value: s.transactionCount24h.toLocaleString() },
    { label: '30d 交易', value: s.transactionCount30d.toLocaleString() },
    { label: '活跃 fact', value: s.factCount.toLocaleString() },
    {
      label: '上次 pull',
      value: s.lastPullAt ? `${relativeTime(s.lastPullAt)} (${s.lastPullStatus})` : '从未',
    },
    {
      label: '上次 push',
      value: s.lastPushAt ? `${relativeTime(s.lastPushAt)} (${s.lastPushStatus})` : '从未',
    },
    { label: 'Pull cursor', value: s.lastPullCursor ?? '—', mono: true },
    { label: 'AI 24h 成本', value: `¥${s.aiCostYuan24h.toFixed(4)} · ${s.aiInvocations24h} 次` },
    {
      label: '24h 失败率',
      value: `${(s.failureRate24h * 100).toFixed(1)}%`,
      warn: s.failureRate24h > 0.1,
    },
  ];
  return (
    <Card>
      <CardHeader>
        <CardTitle>运行状态</CardTitle>
        <CardDescription>过去 24h / 30d 数据接入与 AI 使用</CardDescription>
      </CardHeader>
      <CardContent>
        <div className="grid grid-cols-3 gap-x-4 gap-y-3 text-sm">
          {items.map((it) => (
            <div key={it.label}>
              <div className="text-xs text-muted-foreground">{it.label}</div>
              <div
                className={[
                  'mt-0.5',
                  it.mono ? 'font-mono text-xs' : 'font-medium',
                  it.warn ? 'text-amber-600' : '',
                ]
                  .filter(Boolean)
                  .join(' ')}
              >
                {it.value}
              </div>
            </div>
          ))}
        </div>
      </CardContent>
    </Card>
  );
}


function CallbackConfig({
  data,
  busy,
  onAdd,
  onRemove,
  onSavedUrl,
  onError,
}: {
  data: HostDetail;
  busy: boolean;
  onAdd: () => void;
  onRemove: (suffix: string) => void;
  onSavedUrl: () => void;
  onError: (msg: string) => void;
}) {
  const [draftUrl, setDraftUrl] = useState<string>(data.callbackUrl ?? '');
  const [saving, setSaving] = useState(false);
  const dirty = draftUrl.trim() !== (data.callbackUrl ?? '');

  const saveUrl = async () => {
    setSaving(true);
    try {
      // 空串 → null(关闭回执)
      await hostApi.update({ callbackUrl: draftUrl.trim() === '' ? null : draftUrl.trim() });
      onSavedUrl();
    } catch (e) {
      onError(e instanceof Error ? e.message : String(e));
    } finally {
      setSaving(false);
    }
  };

  return (
    <div className="surface-inset p-3">
      <div className="text-sm font-medium">执行结果回执(PAC → 宿主)</div>
      <div className="mt-1 text-xs text-muted-foreground">
        客服提交召回结果后 PAC POST 回该地址;留空 = 不启用。密钥与 push 相互独立。
      </div>

      {/* callbackUrl */}
      <div className="mt-3 flex items-center gap-2">
        <Input
          compact
          value={draftUrl}
          onChange={(e) => setDraftUrl(e.target.value)}
          placeholder="https://host.example.com/pac/callback"
          className="font-mono"
        />
        <Button size="sm" onClick={saveUrl} disabled={!dirty || saving || busy}>
          {saving ? '保存中…' : '保存'}
        </Button>
      </div>

      {/* callback secrets */}
      <div className="mt-3 flex items-center justify-between">
        <div className="text-xs text-muted-foreground">
          {data.callbackSecrets.length === 0
            ? '尚无回执密钥 —— 添加一条并配好地址后启用'
            : `共 ${data.callbackSecrets.length} 条,索引 0 = current(签名用),其余 grace`}
        </div>
        <Button variant="outline" size="sm" onClick={onAdd} disabled={busy}>
          <Plus className="h-4 w-4" /> Add new
        </Button>
      </div>
      {data.callbackSecrets.length > 0 && (
        <ul className="mt-2 space-y-1.5">
          {data.callbackSecrets.map((s) => (
            <li
              key={s.suffix}
              className="flex items-center justify-between rounded-md row-quiet px-3 py-1.5 text-xs"
            >
              <div className="font-mono">...{s.suffix}</div>
              <div className="flex items-center gap-2">
                <Badge variant={s.role === 'current' ? 'default' : 'outline'}>{s.role}</Badge>
                <Button
                  variant="ghost"
                  size="sm"
                  disabled={busy || data.callbackSecrets.length === 1}
                  onClick={() => onRemove(s.suffix)}
                  title={data.callbackSecrets.length === 1 ? '不能删除唯一一条' : '删除'}
                >
                  <Trash2 className="h-3.5 w-3.5" />
                </Button>
              </div>
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

function ActionUrlsCard({
  urls,
  busy,
  onSaved,
  onError,
}: {
  urls: ActionUrlsConfig;
  busy: boolean;
  onSaved: () => void;
  onError: (msg: string) => void;
}) {
  const [draft, setDraft] = useState<Record<string, string | null>>(() => ({ ...urls }));
  const [saving, setSaving] = useState(false);
  const [selectedKey, setSelectedKey] = useState<string>('');
  const dirty = JSON.stringify(draft) !== JSON.stringify(urls);

  // 没用过的 enum key,给 Select 列出来
  const availableKeys = Object.values(HostActionKey).filter((k) => draft[k] === undefined);

  const save = async () => {
    setSaving(true);
    try {
      await hostApi.update({ actionUrls: draft });
      onSaved();
    } catch (e) {
      onError(e instanceof Error ? e.message : String(e));
    } finally {
      setSaving(false);
    }
  };

  const addKey = () => {
    if (!selectedKey) return;
    setDraft({ ...draft, [selectedKey]: '' });
    setSelectedKey('');
  };

  return (
    <Card>
      <CardHeader>
        <CardTitle>Action URLs(跳转模板)</CardTitle>
        <CardDescription>
          前端按钮跳宿主页面的 URL 模板,支持{' '}
          <code className="text-xs">{'{patientId}'}</code> 等占位变量(见各 key 注释)
        </CardDescription>
      </CardHeader>
      <CardContent className="space-y-3">
        {Object.keys(draft).length === 0 && (
          <div className="text-xs text-muted-foreground">暂无 action URL — 从下方下拉选 key 添加</div>
        )}

        <div className="space-y-2">
          {Object.entries(draft).map(([key, val]) => {
            const meta = (HOST_ACTION_META as Record<string, { label: string; placeholders: string[] }>)[key];
            return (
              <div key={key} className="rounded-md border row-quiet p-2 space-y-1">
                <div className="flex items-center justify-between gap-2">
                  <div className="flex items-center gap-2 min-w-0">
                    <code className="text-xs font-semibold">{key}</code>
                    {meta && <span className="text-xs text-muted-foreground">{meta.label}</span>}
                  </div>
                  <Button
                    variant="ghost"
                    size="sm"
                    onClick={() => {
                      const next = { ...draft };
                      delete next[key];
                      setDraft(next);
                    }}
                  >
                    <Trash2 className="h-3.5 w-3.5" />
                  </Button>
                </div>
                <Input
                  compact
                  value={val ?? ''}
                  onChange={(e) => setDraft({ ...draft, [key]: e.target.value })}
                  placeholder={
                    meta && meta.placeholders.length > 0
                      ? `https://host.example.com/path?${meta.placeholders[0]!.slice(1, -1)}=${meta.placeholders[0]}`
                      : 'https://host.example.com/path'
                  }
                  className="font-mono"
                />
                {meta && meta.placeholders.length > 0 && (
                  <div className="text-[10.5px] text-muted-foreground">
                    可用占位:{meta.placeholders.map((p) => (
                      <code key={p} className="ml-1">{p}</code>
                    ))}
                  </div>
                )}
                {/* 哨兵 postMessage 依赖 HOST_ORIGIN 作 targetOrigin —— 缺配时动作按钮不会渲染 */}
                {isPostMessageSentinel(val) && !(draft.HOST_ORIGIN ?? '').trim() && (
                  <div className="text-[10.5px] text-amber-600">
                    配了 postMessage 但未配 HOST_ORIGIN,该动作按钮不会显示
                  </div>
                )}
              </div>
            );
          })}
        </div>

        {/* Add new */}
        {availableKeys.length > 0 && (
          <div className="flex items-center gap-2 pt-2 border-t">
            <Select value={selectedKey} onValueChange={setSelectedKey}>
              <SelectTrigger className="flex-1">
                <SelectValue placeholder="选择 action 类型" />
              </SelectTrigger>
              <SelectContent>
                {availableKeys.map((k) => (
                  <SelectItem key={k} value={k}>
                    <span className="font-mono text-xs">{k}</span>
                    <span className="ml-2 text-muted-foreground">{HOST_ACTION_META[k].label}</span>
                  </SelectItem>
                ))}
              </SelectContent>
            </Select>
            <Button variant="outline" size="sm" onClick={addKey} disabled={!selectedKey}>
              <Plus className="h-4 w-4" /> Add
            </Button>
          </div>
        )}

        <div className="flex justify-end gap-2 pt-2 border-t">
          <Button
            variant="ghost"
            size="sm"
            onClick={() => setDraft({ ...urls })}
            disabled={!dirty || saving || busy}
          >
            重置
          </Button>
          <Button onClick={save} disabled={!dirty || saving || busy} size="sm">
            {saving ? '保存中…' : '保存'}
          </Button>
        </div>
      </CardContent>
    </Card>
  );
}


function KindPill({ kind }: { kind: string }) {
  const tones: Record<string, string> = {
    pull: 'bg-sky-100 text-sky-700',
    push: 'bg-emerald-100 text-emerald-700',
    reconcile: 'bg-violet-100 text-violet-700',
    cold_import: 'bg-slate-100 text-slate-700',
  };
  return (
    <span
      className={
        'inline-block px-1.5 py-0.5 rounded text-[10.5px] font-medium ' +
        (tones[kind] ?? 'bg-slate-100 text-slate-700')
      }
    >
      {kind}
    </span>
  );
}

function StatusPill({ status }: { status: string }) {
  const tones: Record<string, string> = {
    success: 'bg-emerald-100 text-emerald-700',
    partial: 'bg-amber-100 text-amber-700',
    failed: 'bg-rose-100 text-rose-700',
    running: 'bg-sky-100 text-sky-700',
  };
  return (
    <span
      className={
        'inline-block px-1.5 py-0.5 rounded text-[10.5px] font-medium ' +
        (tones[status] ?? 'bg-slate-100 text-slate-700')
      }
    >
      {status}
    </span>
  );
}

function relativeTime(iso: string): string {
  const diff = Date.now() - new Date(iso).getTime();
  const s = Math.floor(diff / 1000);
  if (s < 60) return `${s}s 前`;
  if (s < 3600) return `${Math.floor(s / 60)}min 前`;
  if (s < 86400) return `${Math.floor(s / 3600)}h 前`;
  return `${Math.floor(s / 86400)}d 前`;
}
