"use client";

import { useEffect, useMemo, useRef, useState } from "react";
import { Download, Eye, ImageIcon, Info, Loader2, Music, RefreshCw, Trash2, X } from "lucide-react";
import { clampPage, pageItems, Pagination } from "@/components/pagination";
import { modalEnter, modalExit, pulseFeedback, revealChildren, runScopedMotion } from "@/lib/ui/motion";
import type { Asset, GenerationJob } from "@/lib/types";

type AssetView = "assets" | "tasks";
const ASSET_PAGE_SIZE = 8;
const TASK_PAGE_SIZE = 8;

export function AssetManager() {
  const [assets, setAssets] = useState<Asset[]>([]);
  const [jobs, setJobs] = useState<GenerationJob[]>([]);
  const [view, setView] = useState<AssetView>("assets");
  const [loading, setLoading] = useState(false);
  const [syncingJobId, setSyncingJobId] = useState<string | null>(null);
  const [deletingAssetId, setDeletingAssetId] = useState<string | null>(null);
  const [deletingJobId, setDeletingJobId] = useState<string | null>(null);
  const [previewAsset, setPreviewAsset] = useState<Asset | null>(null);
  const [expandedJobId, setExpandedJobId] = useState<string | null>(null);
  const [assetPage, setAssetPage] = useState(1);
  const [jobPage, setJobPage] = useState(1);
  const [error, setError] = useState<string | null>(null);
  const managerRef = useRef<HTMLDivElement | null>(null);
  const listRef = useRef<HTMLDivElement | null>(null);
  const feedbackRef = useRef<HTMLDivElement | null>(null);
  const previewDialogRef = useRef<HTMLDivElement | null>(null);
  const closeButtonRef = useRef<HTMLButtonElement | null>(null);

  useEffect(() => {
    refresh().catch(() => undefined);
  }, []);

  const jobByOutputAssetId = useMemo(() => {
    const map = new Map<string, GenerationJob>();
    for (const job of jobs) {
      for (const assetId of job.outputAssetIds) map.set(assetId, job);
    }
    return map;
  }, [jobs]);

  const visibleAssets = pageItems(assets, assetPage, ASSET_PAGE_SIZE);
  const visibleJobs = pageItems(jobs, jobPage, TASK_PAGE_SIZE);

  useEffect(() => {
    setAssetPage((page) => clampPage(page, assets.length, ASSET_PAGE_SIZE));
  }, [assets.length]);

  useEffect(() => {
    setJobPage((page) => clampPage(page, jobs.length, TASK_PAGE_SIZE));
  }, [jobs.length]);

  useEffect(() => {
    if (view === "assets") setAssetPage((page) => clampPage(page, assets.length, ASSET_PAGE_SIZE));
    if (view === "tasks") setJobPage((page) => clampPage(page, jobs.length, TASK_PAGE_SIZE));
  }, [view, assets.length, jobs.length]);

  useEffect(() => {
    return runScopedMotion(managerRef, (scope) => revealChildren(scope));
  }, []);

  useEffect(() => {
    if (listRef.current) revealChildren(listRef.current, ".asset-history-card, .compact-job, .job-detail-panel");
  }, [view, assetPage, jobPage, visibleAssets.length, visibleJobs.length, expandedJobId]);

  useEffect(() => {
    pulseFeedback(feedbackRef.current);
  }, [error]);

  useEffect(() => {
    if (!previewAsset) return undefined;
    modalEnter(previewDialogRef.current);
    window.requestAnimationFrame(() => closeButtonRef.current?.focus());
    const handleKeyDown = (event: KeyboardEvent) => {
      if (event.key === "Escape") closePreview();
    };
    window.addEventListener("keydown", handleKeyDown);
    return () => window.removeEventListener("keydown", handleKeyDown);
  }, [previewAsset]);

  async function refresh() {
    setLoading(true);
    setError(null);
    try {
      const [assetResponse, imageResponse, videoResponse] = await Promise.all([
        fetch("/api/assets", { cache: "no-store" }),
        fetch("/api/generations/image", { cache: "no-store" }),
        fetch("/api/generations/video", { cache: "no-store" })
      ]);
      const [assetPayload, imagePayload, videoPayload] = await Promise.all([
        assetResponse.json(),
        imageResponse.json(),
        videoResponse.json()
      ]);
      if (!assetResponse.ok) throw new Error(assetPayload.error || "读取资产失败");
      if (!imageResponse.ok) throw new Error(imagePayload.error || "读取图片任务失败");
      if (!videoResponse.ok) throw new Error(videoPayload.error || "读取视频任务失败");
      const dedupedJobs = new Map<string, GenerationJob>();
      for (const job of [...(imagePayload.jobs || []), ...(videoPayload.jobs || [])] as GenerationJob[]) {
        dedupedJobs.set(job.id, job);
      }
      const nextJobs = [...dedupedJobs.values()].sort((a, b) => b.createdAt.localeCompare(a.createdAt));
      const outputAssetIds = new Set(nextJobs.flatMap((job) => job.outputAssetIds));
      setAssets((assetPayload.assets || []).filter((asset: Asset) => isResultAsset(asset, outputAssetIds)));
      setJobs(nextJobs);
    } catch (err) {
      setError(err instanceof Error ? err.message : String(err));
    } finally {
      setLoading(false);
    }
  }

  async function syncJob(job: GenerationJob) {
    setSyncingJobId(job.id);
    setError(null);
    try {
      const response = await fetch(jobPath(job), { cache: "no-store" });
      const payload = await response.json();
      if (!response.ok) throw new Error(payload.error || "查询任务失败");
      await refresh();
    } catch (err) {
      setError(err instanceof Error ? err.message : String(err));
    } finally {
      setSyncingJobId(null);
    }
  }

  async function removeAsset(asset: Asset) {
    if (!window.confirm(`删除「${asset.name}」？对应 OSS 对象会一并删除。`)) return;
    setDeletingAssetId(asset.id);
    setError(null);
    try {
      const response = await fetch(`/api/assets/${asset.id}`, { method: "DELETE" });
      const payload = await response.json();
      if (!response.ok) throw new Error(payload.error || "删除资产失败");
      await refresh();
    } catch (err) {
      setError(err instanceof Error ? err.message : String(err));
    } finally {
      setDeletingAssetId(null);
    }
  }

  async function removeJob(job: GenerationJob) {
    if (!window.confirm(`删除「${capabilityLabel(job.capability)}」任务？任务输出结果和对应 OSS 对象会一并删除。`)) return;
    setDeletingJobId(job.id);
    setError(null);
    try {
      const response = await fetch(jobPath(job), { method: "DELETE" });
      const payload = await response.json();
      if (!response.ok) throw new Error(payload.error || "删除任务失败");
      if (expandedJobId === job.id) setExpandedJobId(null);
      await refresh();
    } catch (err) {
      setError(err instanceof Error ? err.message : String(err));
    } finally {
      setDeletingJobId(null);
    }
  }

  function closePreview() {
    modalExit(previewDialogRef.current, () => setPreviewAsset(null));
  }

  return (
    <div className="asset-manager grid" ref={managerRef}>
      <div className="workspace-head" data-animate>
        <div>
          <h1 className="workspace-title">结果资产</h1>
        </div>
      </div>

      <section className="panel" data-animate>
        <div className="asset-manager-toolbar">
          <div className="segmented asset-view-switch" aria-label="结果视图">
            <button type="button" className={view === "assets" ? "active" : ""} aria-pressed={view === "assets"} onClick={() => setView("assets")}>
              资产
            </button>
            <button type="button" className={view === "tasks" ? "active" : ""} aria-pressed={view === "tasks"} onClick={() => setView("tasks")}>
              任务
            </button>
          </div>
          <button className="button" type="button" onClick={refresh} disabled={loading}>
            {loading ? <Loader2 className="spin" size={18} /> : <RefreshCw size={18} />}
            刷新
          </button>
        </div>

        {error ? <div className="callout asset-error" ref={feedbackRef} role="alert">{error}</div> : null}

        {view === "assets" ? (
          <>
            <div className="asset-grid asset-history-grid" ref={listRef}>
              {visibleAssets.map((asset) => (
                <article className="card asset-history-card" key={asset.id}>
                  <button className="asset-preview-button" type="button" onClick={() => setPreviewAsset(asset)} aria-label={`预览 ${asset.name}`}>
                    {renderAssetPreview(asset)}
                  </button>
                  <div className="card-body asset-history-body">
                    <div className="toolbar asset-card-title">
                      <h3>{asset.name}</h3>
                      <span className="status">{sourceLabel(asset.source)}</span>
                    </div>
                    <p className="muted asset-card-meta">{kindLabel(asset)} / {formatDate(asset.createdAt)}</p>
                    <div className="toolbar asset-card-actions">
                      <button className="button mini-button" type="button" title="预览" aria-label={`预览 ${asset.name}`} onClick={() => setPreviewAsset(asset)}>
                        <Eye size={15} />
                      </button>
                      <a className="button mini-button" href={assetDownloadUrl(asset)} title="下载" aria-label={`下载 ${asset.name}`}>
                        <Download size={15} />
                      </a>
                      <button className="button danger mini-button" type="button" title="删除" aria-label={`删除 ${asset.name}`} onClick={() => removeAsset(asset)} disabled={deletingAssetId === asset.id}>
                        {deletingAssetId === asset.id ? <Loader2 className="spin" size={15} /> : <Trash2 size={15} />}
                      </button>
                      {jobByOutputAssetId.get(asset.id) ? (
                        <span className="muted compact-hint">{capabilityLabel(jobByOutputAssetId.get(asset.id)?.capability)}</span>
                      ) : null}
                    </div>
                  </div>
                </article>
              ))}
              {!assets.length ? <div className="callout">暂无生成结果。提交创作、局部重绘或超清后，结果会自动保留在这里。</div> : null}
            </div>
            <Pagination page={assetPage} pageSize={ASSET_PAGE_SIZE} total={assets.length} label="资产分页" onPageChange={setAssetPage} />
          </>
        ) : (
          <>
            <div className="grid task-history-list" ref={listRef}>
              {visibleJobs.map((job) => (
                <article className="card compact-job" key={job.id}>
                  <div className="card-body">
                    <div className="job-summary">
                      <h3>{job.prompt?.slice(0, 90) || capabilityLabel(job.capability)}</h3>
                      <p className="muted">{capabilityLabel(job.capability)} / {job.reqKey}</p>
                      <p className="muted compact-hint">{durationLabel(job)} / 输入 {job.inputAssetIds.length || job.inputUrls.length} 个，输出 {job.outputAssetIds.length} 个</p>
                    </div>
                    <div className="toolbar job-actions">
                      <span className={`status ${job.status}`}>{statusLabel(job.status)}</span>
                      <button className="button mini-button" type="button" aria-expanded={expandedJobId === job.id} onClick={() => setExpandedJobId(expandedJobId === job.id ? null : job.id)}>
                        <Info size={15} />
                        {expandedJobId === job.id ? "收起" : "详情"}
                      </button>
                      <button className="icon-button" title="查询任务" aria-label="查询任务" type="button" onClick={() => syncJob(job)} disabled={syncingJobId === job.id || deletingJobId === job.id}>
                        {syncingJobId === job.id ? <Loader2 className="spin" size={17} /> : <RefreshCw size={17} />}
                      </button>
                      <button className="icon-button" title="删除任务" aria-label="删除任务" type="button" onClick={() => removeJob(job)} disabled={deletingJobId === job.id}>
                        {deletingJobId === job.id ? <Loader2 className="spin" size={17} /> : <Trash2 size={17} />}
                      </button>
                    </div>
                  </div>
                  {expandedJobId === job.id ? <JobDetails job={job} /> : null}
                </article>
              ))}
              {!jobs.length ? <div className="callout">暂无生成任务。提交图片或视频生成后会保留任务历史。</div> : null}
            </div>
            <Pagination page={jobPage} pageSize={TASK_PAGE_SIZE} total={jobs.length} label="任务分页" onPageChange={setJobPage} />
          </>
        )}
      </section>
      {previewAsset ? (
        <div className="asset-preview-backdrop" role="presentation" onMouseDown={closePreview}>
          <div className="asset-preview-dialog" role="dialog" aria-modal="true" aria-label={`预览 ${previewAsset.name}`} ref={previewDialogRef} onMouseDown={(event) => event.stopPropagation()}>
            <div className="asset-preview-head">
              <div>
                <strong>{previewAsset.name}</strong>
                <span className="muted compact-hint">{kindLabel(previewAsset)} / {sourceLabel(previewAsset.source)} / {formatDate(previewAsset.createdAt)}</span>
              </div>
              <div className="asset-preview-actions">
                <a className="icon-button" href={assetDownloadUrl(previewAsset)} title="下载" aria-label={`下载 ${previewAsset.name}`}>
                  <Download size={18} />
                </a>
                <button className="icon-button" type="button" title="关闭预览" aria-label="关闭预览" ref={closeButtonRef} onClick={closePreview}>
                  <X size={18} />
                </button>
              </div>
            </div>
            <div className="asset-preview-media">
              {renderAssetPreviewLarge(previewAsset)}
            </div>
          </div>
        </div>
      ) : null}
    </div>
  );
}

function JobDetails({ job }: { job: GenerationJob }) {
  return (
    <div className="job-detail-panel">
      <dl className="job-detail-grid">
        <div>
          <dt>状态</dt>
          <dd><span className={`status ${job.status}`}>{statusLabel(job.status)}</span></dd>
        </div>
        <div>
          <dt>耗时</dt>
          <dd>{durationValue(job)}</dd>
        </div>
        <div>
          <dt>提交时间</dt>
          <dd>{formatDateTime(job.createdAt)}</dd>
        </div>
        <div>
          <dt>更新时间</dt>
          <dd>{formatDateTime(job.updatedAt)}</dd>
        </div>
        <div>
          <dt>任务类型</dt>
          <dd>{capabilityLabel(job.capability)}</dd>
        </div>
        <div>
          <dt>服务</dt>
          <dd>{providerLabel(job.provider)}</dd>
        </div>
        <div>
          <dt>Req Key</dt>
          <dd>{job.reqKey}</dd>
        </div>
        <div>
          <dt>任务 ID</dt>
          <dd>{job.providerTaskId || job.id}</dd>
        </div>
        <div>
          <dt>输入/输出</dt>
          <dd>{job.inputAssetIds.length || job.inputUrls.length} / {job.outputAssetIds.length}</dd>
        </div>
        {job.error ? (
          <div>
            <dt>错误</dt>
            <dd>{job.error.message}</dd>
          </div>
        ) : null}
      </dl>
      {job.prompt ? (
        <div className="job-prompt-detail">
          <span className="muted compact-hint">提示词</span>
          <p>{job.prompt}</p>
        </div>
      ) : null}
    </div>
  );
}

function renderAssetPreview(asset: Asset) {
  if (isAudio(asset)) {
    return (
      <div className="asset-placeholder asset-kind-placeholder">
        <Music size={24} />
        音频素材
      </div>
    );
  }
  if (asset.kind === "video" || isVideo(asset)) {
    return <video className="asset-thumb" src={asset.url} muted playsInline preload="metadata" />;
  }
  if (asset.kind === "image" || asset.kind === "mask" || asset.kind === "reference" || isImage(asset)) {
    return <img className="asset-thumb" src={asset.url} alt={asset.name} />;
  }
  return (
    <div className="asset-placeholder asset-kind-placeholder">
      <ImageIcon size={24} />
      资产
    </div>
  );
}

function renderAssetPreviewLarge(asset: Asset) {
  if (isAudio(asset)) return <audio src={asset.url} controls />;
  if (asset.kind === "video" || isVideo(asset)) return <video src={asset.url} controls playsInline />;
  if (asset.kind === "image" || asset.kind === "mask" || asset.kind === "reference" || isImage(asset)) {
    return <img src={asset.url} alt={asset.name} />;
  }
  return (
    <div className="asset-placeholder asset-kind-placeholder">
      <ImageIcon size={28} />
      暂不支持预览
    </div>
  );
}

function assetDownloadUrl(asset: Asset) {
  return `/api/assets/${encodeURIComponent(asset.id)}/download`;
}

function isResultAsset(asset: Asset, outputAssetIds: Set<string>) {
  if (!outputAssetIds.has(asset.id)) return false;
  if (asset.kind === "mask" || asset.tags.includes("mask") || typeof asset.metadata.maskRule === "string") return false;
  return asset.source === "generated" || asset.source === "edited" || asset.source === "upscaled";
}

function isImage(asset: Asset) {
  const contentType = getContentType(asset);
  return contentType.startsWith("image/");
}

function isVideo(asset: Asset) {
  const contentType = getContentType(asset);
  return contentType.startsWith("video/") || /\.(mp4|mov|webm)(\?|$)/.test(asset.url.toLowerCase());
}

function isAudio(asset: Asset) {
  const contentType = getContentType(asset);
  return contentType.startsWith("audio/") || /\.(mp3|wav|m4a|aac|flac)(\?|$)/.test(asset.url.toLowerCase());
}

function getContentType(asset: Asset) {
  return typeof asset.metadata.contentType === "string" ? asset.metadata.contentType.toLowerCase() : "";
}

function kindLabel(asset: Asset) {
  if (isAudio(asset)) return "音频";
  if (asset.kind === "video" || isVideo(asset)) return "视频";
  if (asset.kind === "mask") return "Mask";
  return "图片";
}

function sourceLabel(source: Asset["source"]) {
  if (source === "upload") return "上传";
  if (source === "generated") return "生成";
  if (source === "edited") return "重绘";
  if (source === "upscaled") return "超清";
  if (source === "seed") return "示例";
  return "外部";
}

function statusLabel(status: GenerationJob["status"]) {
  if (status === "queued") return "排队中";
  if (status === "running") return "生成中";
  if (status === "succeeded") return "已完成";
  if (status === "failed") return "失败";
  if (status === "expired") return "已过期";
  if (status === "cancelled") return "已取消";
  return status;
}

function providerLabel(provider: GenerationJob["provider"]) {
  if (provider === "volcengine-visual") return "火山视觉";
  if (provider === "evolink") return "EvoLink";
  if (provider === "seedance") return "Seedance";
  return "Mock";
}

function capabilityLabel(capability?: GenerationJob["capability"]) {
  if (capability === "image.generate") return "图片生成";
  if (capability === "image.inpaint") return "局部重绘";
  if (capability === "image.upscale") return "智能超清";
  if (capability === "video.generate") return "视频生成";
  return "生成任务";
}

function jobPath(job: GenerationJob) {
  return job.capability === "video.generate" ? `/api/generations/video/${job.id}` : `/api/generations/image/${job.id}`;
}

function formatDate(value: string) {
  return new Intl.DateTimeFormat("zh-CN", {
    month: "2-digit",
    day: "2-digit",
    hour: "2-digit",
    minute: "2-digit"
  }).format(new Date(value));
}

function formatDateTime(value: string) {
  return new Intl.DateTimeFormat("zh-CN", {
    month: "2-digit",
    day: "2-digit",
    hour: "2-digit",
    minute: "2-digit",
    second: "2-digit"
  }).format(new Date(value));
}

function durationLabel(job: GenerationJob) {
  const terminal = ["succeeded", "failed", "expired", "cancelled"].includes(job.status);
  return `${terminal ? "耗时" : "已等待"} ${durationValue(job)}`;
}

function durationValue(job: GenerationJob) {
  const start = new Date(job.createdAt).getTime();
  const terminal = ["succeeded", "failed", "expired", "cancelled"].includes(job.status);
  const end = terminal ? new Date(job.updatedAt).getTime() : Date.now();
  if (!Number.isFinite(start) || !Number.isFinite(end) || end < start) return "--";
  return formatDuration(end - start);
}

function formatDuration(ms: number) {
  const totalSeconds = Math.max(0, Math.round(ms / 1000));
  const hours = Math.floor(totalSeconds / 3600);
  const minutes = Math.floor((totalSeconds % 3600) / 60);
  const seconds = totalSeconds % 60;
  if (hours) return `${hours}小时${minutes}分`;
  if (minutes) return `${minutes}分${seconds}秒`;
  return `${seconds}秒`;
}
