'use client';

import { useEffect, useRef, useState } from 'react';
import { Upload, Loader2 } from 'lucide-react';
import { toast } from 'sonner';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { hostApi, type ColdImportStatus } from './host-api';

/** BullMQ 终态:轮询到这些状态就停。 */
const TERMINAL = new Set(['completed', 'failed', 'not_found']);
const POLL_MS = 3000;

const STATE_LABEL: Record<string, string> = {
  waiting: '排队中',
  delayed: '排队中',
  active: '导入中',
  completed: '完成',
  failed: '失败',
  not_found: '已结束',
};

function fmtElapsed(sec: number): string {
  const m = Math.floor(sec / 60);
  const s = sec % 60;
  return m > 0 ? `${m}分${s}秒` : `${s}秒`;
}

/**
 * ColdImportCard — 宿主自助上传数据压缩包触发 cold-import。
 *
 * 流程:选 zip → 上传拿 jobId → 每 3s 轮询进度 → 终态停 + 展示 totals + 刷新最近事件。
 * manifest/assemblers 取自 PAC 代码库,上传包只含数据文件(见后端 ImportUploadService)。
 */
export function ColdImportCard({ onDone }: { onDone: () => void }) {
  const inputRef = useRef<HTMLInputElement>(null);
  const [file, setFile] = useState<File | null>(null);
  const [dryRun, setDryRun] = useState(true); // 默认先预检,更安全
  const [uploading, setUploading] = useState(false);
  const [jobId, setJobId] = useState<string | null>(null);
  const [status, setStatus] = useState<ColdImportStatus | null>(null);
  const [startedAt, setStartedAt] = useState<number | null>(null);
  const [elapsed, setElapsed] = useState(0);

  // onDone 存 ref,避免父组件重渲染导致轮询 effect 被重置
  const onDoneRef = useRef(onDone);
  useEffect(() => {
    onDoneRef.current = onDone;
  }, [onDone]);

  // 轮询:仅依赖 jobId
  useEffect(() => {
    if (!jobId) return;
    let timer: ReturnType<typeof setInterval> | null = null;
    let stopped = false;

    const poll = async () => {
      try {
        const s = await hostApi.getImportStatus(jobId);
        if (stopped) return;
        setStatus(s);
        if (TERMINAL.has(s.state)) {
          if (timer) clearInterval(timer);
          timer = null;
          if (s.state === 'completed') {
            if (s.result?.dryRun) {
              // 预检:未落库,不刷新最近事件
              toast.success('预检完成(未落库),检查下方 failed / 数量再正式导入');
            } else {
              toast.success('导入完成');
              onDoneRef.current();
            }
          } else if (s.state === 'failed') {
            toast.error(`导入失败:${s.failedReason ?? '未知原因'}`);
          } else {
            toast.message('任务已不在队列(可能已完成并被清理)');
          }
        }
      } catch {
        // 单次轮询失败不打断,等下一轮
      }
    };

    void poll();
    timer = setInterval(() => void poll(), POLL_MS);
    return () => {
      stopped = true;
      if (timer) clearInterval(timer);
    };
  }, [jobId]);

  const running = jobId != null && (!status || !TERMINAL.has(status.state));

  // 已用时长:running 时每秒 tick
  useEffect(() => {
    if (!running || startedAt == null) return;
    const t = setInterval(() => setElapsed(Math.floor((Date.now() - startedAt) / 1000)), 1000);
    return () => clearInterval(t);
  }, [running, startedAt]);

  const onUpload = async () => {
    if (!file) return;
    setUploading(true);
    setStatus(null);
    setJobId(null);
    try {
      const form = new FormData();
      form.append('file', file);
      form.append('dryRun', String(dryRun));
      const { jobId: id } = await hostApi.importArchive(form);
      setJobId(id);
      setStartedAt(Date.now());
      setElapsed(0);
      toast.success(dryRun ? '已上传,开始预检(不落库)' : '已上传,开始后台导入');
    } catch (e) {
      toast.error(e instanceof Error ? e.message : '上传失败');
    } finally {
      setUploading(false);
    }
  };

  const t = status?.result?.totals;

  return (
    <Card>
      <CardHeader>
        <CardTitle>数据导入</CardTitle>
        <CardDescription>
          上传数据压缩包(zip,含 manifest 声明的 CSV/JSON)触发整包 cold-import。manifest / assemblers
          取自 PAC 侧无需上传。重复上传同一批数据是安全的(幂等空转:去重高、fact 新建≈0)。
        </CardDescription>
      </CardHeader>
      <CardContent className="space-y-3">
        <input
          ref={inputRef}
          type="file"
          accept=".zip"
          className="hidden"
          onChange={(e) => setFile(e.target.files?.[0] ?? null)}
        />
        <div className="flex flex-wrap items-center gap-3">
          <Button
            variant="outline"
            onClick={() => inputRef.current?.click()}
            disabled={uploading || running}
          >
            选择 zip
          </Button>
          <span className="min-w-0 flex-1 truncate text-sm text-muted-foreground">
            {file ? file.name : '未选择文件'}
          </span>
          <label className="flex items-center gap-1.5 text-sm text-muted-foreground">
            <input
              type="checkbox"
              checked={dryRun}
              onChange={(e) => setDryRun(e.target.checked)}
              disabled={uploading || running}
            />
            仅预检(不落库)
          </label>
          <Button onClick={onUpload} disabled={!file || uploading || running}>
            {uploading ? (
              <Loader2 className="mr-2 h-4 w-4 animate-spin" />
            ) : (
              <Upload className="mr-2 h-4 w-4" />
            )}
            {dryRun ? '预检' : '上传并导入'}
          </Button>
        </div>

        {jobId && (
          <div className="rounded-md border p-3 text-sm">
            <div className="flex items-center gap-2">
              {running && <Loader2 className="h-4 w-4 animate-spin" />}
              <span>
                状态:{STATE_LABEL[status?.state ?? ''] ?? status?.state ?? '排队中'}
                {status?.result?.dryRun ? '(预检)' : ''}
              </span>
              {(running || elapsed > 0) && (
                <span className="text-xs text-muted-foreground">· 用时 {fmtElapsed(elapsed)}</span>
              )}
            </div>

            {/* 运行中真实进度条(cohortDone/cohortTotal 批次) */}
            {status?.progress && status.progress.total > 0 && (
              <div className="mt-2">
                <div className="h-1.5 w-full overflow-hidden rounded bg-muted">
                  <div
                    className="h-full bg-primary transition-all"
                    style={{
                      width: `${Math.round((status.progress.done / status.progress.total) * 100)}%`,
                    }}
                  />
                </div>
                <div className="mt-1 text-xs text-muted-foreground">
                  已处理 {status.progress.done}/{status.progress.total} 批
                </div>
              </div>
            )}

            {/* 长任务提示:后台跑,可离开 */}
            {running && (
              <p className="mt-1.5 text-xs text-muted-foreground">
                导入在后台进行,可离开此页,稍后回来查看结果。
              </p>
            )}

            {status?.state === 'failed' && status.failedReason && (
              <p className="mt-1 text-destructive">失败原因:{status.failedReason}</p>
            )}
            {t && (
              <div className="mt-2 grid grid-cols-2 gap-x-4 gap-y-1 text-muted-foreground">
                <span>患者 upsert:{t.patientsUpserted}</span>
                <span>事务写入:{t.transactionsWritten}</span>
                <span>幂等去重:{t.duplicates}</span>
                <span>失败:{t.failed}</span>
                <span>fact 新建:{t.factsCreated}</span>
                <span>fact 更新:{t.factsSuperseded}</span>
                <span>fact 未变:{t.factsUnchanged}</span>
                <span>证据追加:{t.factsEvidenceAppended}</span>
              </div>
            )}
          </div>
        )}
      </CardContent>
    </Card>
  );
}
