import { useRef, useState, useEffect, useCallback } from 'react';
import { createPortal } from 'react-dom';
import type { GenerationTask } from '../types';
import { useGenerationStore } from '../store/generation';
import { showToast } from './Toast';
import { ConfirmModal } from './ConfirmModal';
import { tosThumb, rewriteTosUrl } from '../lib/api';
import styles from './GenerationCard.module.css';

const EditIcon = () => (
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
    <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" />
    <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" />
  </svg>
);

const RefreshIcon = () => (
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
    <polyline points="23 4 23 10 17 10" />
    <path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10" />
  </svg>
);

const VideoIcon = () => (
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
    <polygon points="23 7 16 12 23 17 23 7" />
    <rect x="1" y="5" width="15" height="14" rx="2" ry="2" />
  </svg>
);

const DownloadIcon = () => (
  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
    <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
    <polyline points="7 10 12 15 17 10" />
    <line x1="12" y1="15" x2="12" y2="3" />
  </svg>
);

// Mention tag with thumbnail + hover preview
function MentionTag({ label, thumbUrl, assetType }: { label: string; thumbUrl?: string; assetType?: string }) {
  const [hover, setHover] = useState(false);
  const [thumbBroken, setThumbBroken] = useState(false);
  const ref = useRef<HTMLSpanElement>(null);
  const [pos, setPos] = useState({ top: 0, left: 0 });
  const isAudio = assetType === 'Audio' || assetType === 'audio';
  const isVideo = assetType === 'Video' || assetType === 'video';
  const showThumb = thumbUrl && !thumbBroken;

  return (
    <>
      <span
        ref={ref}
        className={styles.mentionTag}
        onMouseEnter={() => {
          if (!isAudio && showThumb && ref.current) {
            const rect = ref.current.getBoundingClientRect();
            setPos({ top: rect.top - 8, left: rect.left + rect.width / 2 });
            setHover(true);
          }
        }}
        onMouseLeave={() => setHover(false)}
      >
        {isAudio ? (
          <span style={{ marginRight: 3, fontSize: 13, verticalAlign: 'middle' }}>♫</span>
        ) : showThumb ? (
          <img
            src={tosThumb(thumbUrl, 28)}
            alt=""
            style={{ width: 14, height: 14, borderRadius: 3, objectFit: 'cover', verticalAlign: 'middle', marginRight: 3 }}
            onError={() => setThumbBroken(true)}
          />
        ) : isVideo ? (
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" style={{ verticalAlign: 'middle', marginRight: 3, opacity: 0.6 }}>
            <rect x="2" y="4" width="20" height="16" rx="2" />
            <path d="M10 9l5 3-5 3V9z" fill="currentColor" stroke="none" />
          </svg>
        ) : (
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" style={{ verticalAlign: 'middle', marginRight: 3, opacity: 0.6 }}>
            <rect x="3" y="3" width="18" height="18" rx="2" />
            <circle cx="8.5" cy="8.5" r="1.5" fill="currentColor" stroke="none" />
            <path d="M21 15l-5-5L5 21" />
          </svg>
        )}
        {label}
      </span>
      {hover && showThumb && createPortal(
        <div className={styles.mentionPreview} style={{ top: pos.top, left: pos.left }}>
          <img src={tosThumb(thumbUrl, 200)} alt={label} className={styles.mentionPreviewImg} onError={(e) => { (e.target as HTMLImageElement).style.display = 'none'; }} />
          <div className={styles.mentionPreviewLabel}>{label}</div>
        </div>,
        document.body
      )}
    </>
  );
}

// Render prompt text with @mentions as styled tags (thumbnail + hover preview)
export function renderPromptWithMentions(
  text: string,
  assetMentions: Record<string, unknown>[],
  references: { label: string; previewUrl?: string }[]
) {
  // Build lookup: label → { thumbUrl, assetType }
  const thumbMap = new Map<string, { thumbUrl: string; assetType: string }>();
  for (const am of assetMentions) {
    if (am.label) thumbMap.set(am.label as string, {
      thumbUrl: (am.thumbUrl as string) || '',
      assetType: (am.assetType as string) || 'image',
    });
  }
  for (const r of references) {
    if (r.label && !thumbMap.has(r.label)) thumbMap.set(r.label, {
      thumbUrl: r.previewUrl || '',
      assetType: (r as Record<string, unknown>).type as string || 'image',
    });
  }

  const labels = [...thumbMap.keys()];
  if (labels.length === 0) return text;

  // Build regex: match @label patterns, longest first
  labels.sort((a, b) => b.length - a.length);
  const escaped = labels.map((l) => l.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
  const regex = new RegExp(`(@(?:${escaped.join('|')}))`, 'g');

  const parts = text.split(regex);
  if (parts.length === 1) return text;

  return parts.map((part, i) => {
    if (regex.test(part)) {
      regex.lastIndex = 0;
      const label = part.slice(1); // remove @
      const info = thumbMap.get(label);
      return <MentionTag key={i} label={label} thumbUrl={info?.thumbUrl} assetType={info?.assetType} />;
    }
    regex.lastIndex = 0;
    return part;
  });
}

interface Props {
  task: GenerationTask;
  onOpenDetail?: (task: GenerationTask) => void;
}

export function GenerationCard({ task, onOpenDetail }: Props) {
  const removeTask = useGenerationStore((s) => s.removeTask);
  const reEdit = useGenerationStore((s) => s.reEdit);
  const regenerate = useGenerationStore((s) => s.regenerate);
  const toggleFavorite = useGenerationStore((s) => s.toggleFavorite);

  const videoRef = useRef<HTMLVideoElement>(null);
  const moreRef = useRef<HTMLDivElement>(null);
  const promptLineRef = useRef<HTMLDivElement>(null);
  const promptWrapperRef = useRef<HTMLDivElement>(null);
  const labelsRef = useRef<HTMLSpanElement>(null);
  const refColumnRef = useRef<HTMLDivElement>(null);
  const [videoHover, setVideoHover] = useState(false);
  const [promptHover, setPromptHover] = useState(false);
  const [showMore, setShowMore] = useState(false);
  const [truncatedPrompt, setTruncatedPrompt] = useState(task.prompt);
  const [confirmDelete, setConfirmDelete] = useState(false);
  const [detailHover, setDetailHover] = useState(false);
  const [detailPos, setDetailPos] = useState({ top: 0, right: 0 });
  const detailLinkRef = useRef<HTMLSpanElement>(null);
  const detailLeaveTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
  const [refPreview, setRefPreview] = useState<{ url: string; label: string; type: string; top: number; left: number; isAssetRef?: boolean } | null>(null);

  const startDetailLeave = useCallback(() => {
    if (detailLeaveTimer.current) clearTimeout(detailLeaveTimer.current);
    detailLeaveTimer.current = setTimeout(() => setDetailHover(false), 200);
  }, []);
  const cancelDetailLeave = useCallback(() => {
    if (detailLeaveTimer.current) clearTimeout(detailLeaveTimer.current);
  }, []);

  // Close more menu on click outside
  useEffect(() => {
    if (!showMore) return;
    const handler = (e: MouseEvent) => {
      if (moreRef.current && !moreRef.current.contains(e.target as Node)) {
        setShowMore(false);
      }
    };
    document.addEventListener('mousedown', handler);
    return () => document.removeEventListener('mousedown', handler);
  }, [showMore]);

  // JS-level prompt truncation: ensure labels always visible at end of line 2
  const computeTruncation = useCallback(() => {
    const container = promptLineRef.current;
    const labelsEl = labelsRef.current;
    if (!container || !labelsEl) return;

    const containerWidth = container.offsetWidth;
    if (containerWidth === 0) return;

    const style = getComputedStyle(container);
    const font = `${style.fontSize} ${style.fontFamily}`;
    const labelsWidth = labelsEl.offsetWidth + 8;
    // Account for mention tags (thumbnails) taking extra width vs plain text
    const mentionCount = (task.assetMentions?.length || 0) + (task.references?.length || 0);
    const mentionExtraWidth = mentionCount * 24; // ~24px extra per mention (thumbnail + padding)
    const totalAvailable = containerWidth * 2 - labelsWidth - 24 - mentionExtraWidth;

    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d')!;
    ctx.font = font;

    const prompt = task.prompt || '';
    const fullWidth = ctx.measureText(prompt).width;
    if (fullWidth <= totalAvailable) {
      setTruncatedPrompt(prompt);
      return;
    }

    let truncated = '';
    let totalWidth = 0;
    const ellipsisWidth = ctx.measureText('…').width;
    for (const char of prompt) {
      const charWidth = ctx.measureText(char).width;
      if (totalWidth + charWidth + ellipsisWidth > totalAvailable) {
        break;
      }
      truncated += char;
      totalWidth += charWidth;
    }
    setTruncatedPrompt(truncated + '…');
  }, [task.prompt]);

  useEffect(() => {
    computeTruncation();
    const container = promptLineRef.current;
    if (!container) return;
    const ro = new ResizeObserver(() => computeTruncation());
    ro.observe(container);
    return () => ro.disconnect();
  }, [computeTruncation]);

  const isGenerating = task.status === 'generating';
  const hasResult = task.status === 'completed' && !!task.resultUrl;

  const handleVideoMouseEnter = () => {
    setVideoHover(true);
    const v = videoRef.current;
    if (!v) return;
    v.muted = false;
    v.play().catch(() => {
      // Browser blocks unmuted autoplay — fallback to muted
      v.muted = true;
      v.play().catch(() => {});
    });
  };

  const handleVideoMouseLeave = () => {
    setVideoHover(false);
    const v = videoRef.current;
    if (!v) return;
    v.pause();
    v.currentTime = 0;
  };

  const handleDownload = async (e: React.MouseEvent) => {
    e.stopPropagation();
    if (!task.resultUrl) return;
    try {
      const res = await fetch(task.resultUrl);
      const blob = await res.blob();
      const blobUrl = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = blobUrl;
      a.download = `airdrama-${task.id}.mp4`;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      setTimeout(() => URL.revokeObjectURL(blobUrl), 10000);
    } catch {
      const a = document.createElement('a');
      a.href = task.resultUrl;
      a.download = `airdrama-${task.id}.mp4`;
      a.click();
    }
  };

  const handleCopyPrompt = (e: React.MouseEvent) => {
    e.stopPropagation();
    navigator.clipboard.writeText(task.prompt).then(() => {
      showToast('已复制');
    });
  };

  const handleVideoClick = () => {
    if (hasResult && onOpenDetail) {
      onOpenDetail(task);
    }
  };

  return (
    <div className={styles.card}>
      {/* Header: reference thumbnails + prompt + meta labels */}
      <div className={styles.header}>
        {/* Left: reference thumbnails */}
        {task.references.length > 0 && (
          <div ref={refColumnRef} className={styles.refColumn}>
            {task.references.map((ref) => (
              <div
                key={ref.id}
                className={styles.refThumb}
                onMouseEnter={(e) => {
                  if (ref.type === 'audio') return;
                  const rect = e.currentTarget.getBoundingClientRect();
                  setRefPreview({ url: ref.previewUrl, label: ref.label, type: ref.type, top: rect.top - 8, left: rect.left + rect.width / 2, isAssetRef: ref.isAssetRef });
                }}
                onMouseLeave={() => setRefPreview(null)}
              >
                {ref.type === 'video' && !ref.isAssetRef ? (
                  <video src={rewriteTosUrl(ref.previewUrl)} className={styles.refMedia} muted />
                ) : ref.type === 'audio' ? (
                  <div className={styles.audioThumb}>
                    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round">
                      <path d="M9 18V5l12-2v13" />
                      <circle cx="6" cy="18" r="3" />
                      <circle cx="18" cy="16" r="3" />
                    </svg>
                  </div>
                ) : (
                  <img src={tosThumb(ref.previewUrl, 112)} alt={ref.label} className={styles.refMedia} onError={(e) => { (e.target as HTMLImageElement).style.display = 'none'; }} />
                )}
              </div>
            ))}
          </div>
        )}
        {/* Right: prompt + inline labels */}
        <div className={styles.headerRight}>
          <div
            ref={promptWrapperRef}
            className={styles.promptWrapper}
            onMouseLeave={() => { setPromptHover(false); startDetailLeave(); }}
          >
            {/* 默认状态：截断提示词 + inline 标签 */}
            <div ref={promptLineRef} className={styles.promptLine}>
              <span onMouseEnter={() => setPromptHover(true)}>
                {renderPromptWithMentions(truncatedPrompt || '(无文字描述)', task.assetMentions || [], task.references)}
              </span>
              <span
                ref={labelsRef}
                className={styles.labelsInline}
                onMouseEnter={() => setPromptHover(false)}
              >
                <span className={styles.label}>
                  {task.model === 'seedance_2.0' ? 'AirDrama' : 'AirDrama Fast'}
                </span>
                <span className={styles.label}>{task.duration}s</span>
                <span className={styles.label}>{task.aspectRatio}</span>
                <span
                  ref={detailLinkRef}
                  className={styles.detailLink}
                  onMouseEnter={() => {
                    cancelDetailLeave();
                    const el = detailLinkRef.current;
                    if (el) {
                      const rect = el.getBoundingClientRect();
                      setDetailPos({
                        top: rect.bottom + 8,
                        right: window.innerWidth - rect.right,
                      });
                    }
                    setDetailHover(true);
                  }}
                  onMouseLeave={startDetailLeave}
                >
                  详细信息 ⓘ
                </span>
              </span>
            </div>
          </div>
          {/* 详细信息弹窗 — 放在 promptWrapper 外，鼠标可以移到弹窗上 */}
          {detailHover && (
            <div
              className={styles.detailTooltip}
              style={{ top: detailPos.top, right: detailPos.right }}
              onMouseEnter={() => { cancelDetailLeave(); setDetailHover(true); }}
              onMouseLeave={startDetailLeave}
            >
              <div className={styles.detailRow}>
                <span>视频比例</span><span>{task.aspectRatio}</span>
              </div>
              <div className={styles.detailRow}>
                <span>时长</span><span>{task.duration}s</span>
              </div>
              <div className={styles.detailRow}>
                <span>分辨率</span><span>{task.resolution.toUpperCase()}</span>
              </div>
              <div className={styles.detailRow}>
                <span>模型</span>
                <span>{task.model === 'seedance_2.0' ? 'AirDrama' : 'AirDrama Fast'}</span>
              </div>
              <div className={styles.detailRow}>
                <span>生成时间</span>
                <span>{new Date(task.createdAt).toLocaleString('zh-CN')}</span>
              </div>
              {(task.tokensConsumed ?? 0) > 0 && (
                <>
                  <div className={styles.detailRow}>
                    <span>消耗 Tokens</span>
                    <span>{(task.tokensConsumed ?? 0).toLocaleString()}</span>
                  </div>
                  <div className={styles.detailRow}>
                    <span>费用</span>
                    <span>¥{(task.costAmount ?? 0).toFixed(2)}</span>
                  </div>
                </>
              )}
              {(task.seed ?? -1) > 0 && (
                <div className={styles.detailRow}>
                  <span>种子值</span>
                  <span>{task.seed}</span>
                </div>
              )}
            </div>
          )}
        </div>
        {/* hover 展开黑底：基于 header 定位，左边距图片 4px */}
        {promptHover && task.prompt && (
          <div
            className={styles.promptExpanded}
            style={{ left: refColumnRef.current ? refColumnRef.current.offsetWidth + 4 : 0 }}
            onMouseEnter={() => setPromptHover(true)}
            onMouseLeave={() => setPromptHover(false)}
          >
            {renderPromptWithMentions(task.prompt, task.assetMentions || [], task.references)}
          </div>
        )}
      </div>

      {/* Reference thumbnail hover preview */}
      {refPreview && createPortal(
        <div className={styles.mentionPreview} style={{ top: refPreview.top, left: refPreview.left }}>
          {refPreview.type === 'video' && !refPreview.isAssetRef ? (
            <video src={rewriteTosUrl(refPreview.url)} className={styles.mentionPreviewImg} autoPlay loop muted playsInline />
          ) : (
            <img src={tosThumb(refPreview.url, 300)} alt={refPreview.label} className={styles.mentionPreviewImg} onError={(e) => { (e.target as HTMLImageElement).style.display = 'none'; }} />
          )}
          <div className={styles.mentionPreviewLabel}>{refPreview.label}</div>
        </div>,
        document.body
      )}

      {/* Video / result area */}
      <div className={styles.content}>
        {isGenerating ? (
          <div className={styles.resultArea}>
            <div className={styles.shimmerBg} />
            <div className={styles.generating}>
              <div className={styles.loadingSpinner} />
              <span className={styles.loadingText}>视频生成中…</span>
              <div className={styles.progressBar}>
                <div
                  className={styles.progressFill}
                  style={{ width: `${task.progress}%` }}
                />
              </div>
              <span className={styles.progressText}>{Math.round(task.progress)}%</span>
            </div>
          </div>
        ) : task.status === 'failed' ? (
          <p className={styles.errorText}>{task.errorMessage || '生成失败，请重试'}</p>
        ) : task.resultUrl ? (
          <div
            className={styles.resultArea}
            onMouseEnter={handleVideoMouseEnter}
            onMouseLeave={handleVideoMouseLeave}
            onClick={handleVideoClick}
            style={{ cursor: 'pointer' }}
          >
            <video
              ref={videoRef}
              src={rewriteTosUrl(task.resultUrl)}
              poster={task.thumbnailUrl ? rewriteTosUrl(task.thumbnailUrl) : undefined}
              className={styles.resultMedia}
              loop
              preload="metadata"
            />
            {videoHover && (
              <div className={styles.videoOverlay}>
                <button className={styles.downloadBtn} onClick={handleDownload}>
                  <DownloadIcon />
                </button>
                <button className={styles.downloadBtn} onClick={(e) => { e.stopPropagation(); toggleFavorite(task.id); }}>
                  <svg width="18" height="18" viewBox="0 0 24 24" fill={task.isFavorited ? '#faad14' : 'none'} stroke={task.isFavorited ? '#faad14' : 'currentColor'} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
                    <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" />
                  </svg>
                </button>
              </div>
            )}
          </div>
        ) : (
          <div className={styles.resultArea}>
            <div className={styles.resultPlaceholder}>
              <VideoIcon />
              <span>视频已生成</span>
            </div>
          </div>
        )}
      </div>

      {/* Bottom action buttons */}
      {isGenerating && (
        <div className={styles.actions}>
          <button className={styles.actionBtn} onClick={() => reEdit(task.id)}>
            <EditIcon /> <span>重新编辑</span>
          </button>
        </div>
      )}
      {!isGenerating && (
        <div className={styles.actions}>
          <button className={styles.actionBtn} onClick={() => reEdit(task.id)}>
            <EditIcon /> <span>重新编辑</span>
          </button>
          <button className={styles.actionBtn} onClick={() => regenerate(task.id)}>
            <RefreshIcon /> <span>再次生成</span>
          </button>
          <div className={styles.moreMenu} ref={moreRef}>
            <button className={styles.moreBtn} onClick={() => setShowMore(!showMore)}>
              <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
                <circle cx="5" cy="12" r="2" />
                <circle cx="12" cy="12" r="2" />
                <circle cx="19" cy="12" r="2" />
              </svg>
            </button>
            {showMore && (
              <div className={styles.moreDropdown}>
                <button onClick={() => { setConfirmDelete(true); setShowMore(false); }}>
                  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
                    <polyline points="3 6 5 6 21 6" />
                    <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
                  </svg>
                  删除
                </button>
              </div>
            )}
          </div>
        </div>
      )}

      <ConfirmModal
        open={confirmDelete}
        title="删除视频"
        message="确定要删除这条生成记录吗？此操作不可撤销。"
        confirmText="删除"
        danger
        onConfirm={() => { removeTask(task.id); setConfirmDelete(false); }}
        onCancel={() => setConfirmDelete(false)}
      />
    </div>
  );
}
