import { memo, useCallback, useEffect, useRef, useState, type CSSProperties, type ChangeEvent } from "react";
import {
  AppstoreOutlined,
  CaretRightOutlined,
  ClockCircleOutlined,
  CopyOutlined,
  DownloadOutlined,
  FileTextOutlined,
  FullscreenOutlined,
  MutedOutlined,
  PauseOutlined,
  PictureOutlined,
  ReloadOutlined,
  SoundOutlined,
  StopOutlined,
  ThunderboltOutlined,
} from "@ant-design/icons";
import { assetClient } from "../../../api/assetClient";
import { communityClient } from "../../../api/communityClient";
import { saveAssetToLocalLibrary } from "../../assets/localAssetStore";
import { SmoothedProgressBar } from "../SmoothedProgressBar";
import {
  DEFAULT_IMAGE_GENERATION_EXPECTED_DURATION_MS,
  DEFAULT_SUPER_RESOLUTION_EXPECTED_DURATION_MS,
  DEFAULT_VIDEO_GENERATION_EXPECTED_DURATION_MS,
  formatEstimatedRemainingLabel,
  useSmoothedProgress,
} from "../../../hooks/useSmoothedProgress";
import { renderMarkdownBlocks } from "../markdownRenderer";
import { downloadResultAsset } from "../workbenchDownload";
import type { WorkbenchChatAttachment, WorkbenchChatMessage, WorkbenchResultActionPayload } from "../workbenchChatTypes";

// ─── Helpers ──────────────────────────────────────────────────────────────────

function cleanModelDisplayLabel(label: string) {
  const channelName = "百炼";
  return label.replace(new RegExp(`\\s*[（(]\\s*${channelName}\\s*[）)]`, "g"), "").trim();
}

function formatMediaTime(value: number) {
  if (!Number.isFinite(value) || value <= 0) return "0:00";
  const minutes = Math.floor(value / 60);
  const seconds = Math.floor(value % 60);
  return `${minutes}:${String(seconds).padStart(2, "0")}`;
}

// ─── MarkdownMessage ──────────────────────────────────────────────────────────

export const MarkdownMessage = memo(function MarkdownMessage({ text }: { text: string }) {
  const normalized = text.trim();
  if (!normalized) return null;
  return <div className="ai-chat-markdown">{renderMarkdownBlocks(normalized)}</div>;
});

// ─── ImmersiveVideoPlayer ─────────────────────────────────────────────────────

export const ImmersiveVideoPlayer = memo(function ImmersiveVideoPlayer({
  src,
  title,
}: {
  src: string;
  title: string;
}) {
  const videoRef = useRef<HTMLVideoElement | null>(null);
  const frameRef = useRef<HTMLDivElement | null>(null);
  const [isPlaying, setIsPlaying] = useState(false);
  const [isMuted, setIsMuted] = useState(false);
  const [isFullscreen, setIsFullscreen] = useState(false);
  const [duration, setDuration] = useState(0);
  const [currentTime, setCurrentTime] = useState(0);
  const [volume, setVolume] = useState(1);

  const syncVideoState = useCallback(() => {
    const video = videoRef.current;
    if (!video) return;
    setIsPlaying(!video.paused && !video.ended);
    setIsMuted(video.muted);
    setVolume(video.volume);
    setCurrentTime(video.currentTime || 0);
    setDuration(Number.isFinite(video.duration) ? video.duration : 0);
  }, []);

  useEffect(() => {
    const video = videoRef.current;
    if (!video) return undefined;

    video.volume = volume;
    syncVideoState();

    const events: Array<keyof HTMLMediaElementEventMap> = [
      "loadedmetadata",
      "durationchange",
      "timeupdate",
      "play",
      "pause",
      "ended",
      "volumechange",
    ];
    events.forEach((eventName) => video.addEventListener(eventName, syncVideoState));
    return () => events.forEach((eventName) => video.removeEventListener(eventName, syncVideoState));
  }, [src, syncVideoState, volume]);

  useEffect(() => {
    const handleFullscreenChange = () => {
      setIsFullscreen(document.fullscreenElement === frameRef.current);
    };

    document.addEventListener("fullscreenchange", handleFullscreenChange);
    return () => document.removeEventListener("fullscreenchange", handleFullscreenChange);
  }, []);

  const togglePlayback = useCallback(() => {
    const video = videoRef.current;
    if (!video) return;
    if (video.paused || video.ended) {
      void video.play().catch(() => {});
    } else {
      video.pause();
    }
  }, []);

  const handleSeek = (event: ChangeEvent<HTMLInputElement>) => {
    const video = videoRef.current;
    const nextTime = Number(event.currentTarget.value);
    if (!video || !Number.isFinite(nextTime)) return;
    video.currentTime = nextTime;
    setCurrentTime(nextTime);
  };

  const handleVolumeChange = (event: ChangeEvent<HTMLInputElement>) => {
    const video = videoRef.current;
    const nextVolume = Number(event.currentTarget.value);
    if (!video || !Number.isFinite(nextVolume)) return;
    video.volume = nextVolume;
    video.muted = nextVolume === 0;
    setVolume(nextVolume);
    setIsMuted(video.muted);
  };

  const toggleMute = useCallback(() => {
    const video = videoRef.current;
    if (!video) return;
    video.muted = !video.muted;
    setIsMuted(video.muted);
  }, []);

  const toggleFullscreen = useCallback(() => {
    const frame = frameRef.current;
    if (!frame) return;
    if (document.fullscreenElement === frame) {
      void document.exitFullscreen?.();
    } else {
      void frame.requestFullscreen?.();
    }
  }, []);

  const progress = duration > 0 ? Math.min(100, Math.max(0, (currentTime / duration) * 100)) : 0;
  const volumeProgress = Math.min(100, Math.max(0, volume * 100));

  return (
    <div
      ref={frameRef}
      className={`ai-chat-media-player${isPlaying ? " is-playing" : ""}${isFullscreen ? " is-fullscreen" : ""}`}
    >
      <div className="ai-chat-media-player__stage" onClick={togglePlayback}>
        <video
          ref={videoRef}
          key={src}
          src={src}
          playsInline
          preload="metadata"
          aria-label={title}
        />
        <span className="ai-chat-media-player__vignette" />
        <button
          type="button"
          className="ai-chat-media-player__center-play"
          onClick={(event) => {
            event.stopPropagation();
            togglePlayback();
          }}
          aria-label={isPlaying ? "Pause video" : "Play video"}
        >
          {isPlaying ? <PauseOutlined /> : <CaretRightOutlined />}
        </button>
      </div>
      <div className="ai-chat-media-player__controls" onClick={(event) => event.stopPropagation()}>
        <input
          className="ai-chat-media-player__seek"
          type="range"
          min="0"
          max={duration || 0}
          step="0.01"
          value={Math.min(currentTime, duration || currentTime)}
          onChange={handleSeek}
          disabled={duration <= 0}
          aria-label="Video progress"
          style={{ "--progress": `${progress}%` } as CSSProperties}
        />
        <div className="ai-chat-media-player__bar">
          <button type="button" onClick={togglePlayback} aria-label={isPlaying ? "Pause video" : "Play video"}>
            {isPlaying ? <PauseOutlined /> : <CaretRightOutlined />}
          </button>
          <span className="ai-chat-media-player__time">
            {formatMediaTime(currentTime)} / {formatMediaTime(duration)}
          </span>
          <span className="ai-chat-media-player__spacer" />
          <button type="button" onClick={toggleMute} aria-label={isMuted ? "Unmute video" : "Mute video"}>
            {isMuted || volume === 0 ? <MutedOutlined /> : <SoundOutlined />}
          </button>
          <input
            className="ai-chat-media-player__volume"
            type="range"
            min="0"
            max="1"
            step="0.01"
            value={isMuted ? 0 : volume}
            onChange={handleVolumeChange}
            aria-label="Video volume"
            style={{ "--progress": `${isMuted ? 0 : volumeProgress}%` } as CSSProperties}
          />
          <button type="button" onClick={toggleFullscreen} aria-label="Fullscreen video">
            <FullscreenOutlined />
          </button>
        </div>
      </div>
    </div>
  );
});

// ─── ResultCard ───────────────────────────────────────────────────────────────

export const ResultCard = memo(function ResultCard({
  message,
  onRegenerate,
  onOpenMedia,
  onSuperResolveVideo,
  onSuperResolveImage,
  onOpenResultInCanvas,
  downloadFilenameBase,
}: {
  message: WorkbenchChatMessage;
  onRegenerate: (message: WorkbenchChatMessage) => void;
  onOpenMedia: (item: WorkbenchChatAttachment) => void;
  onSuperResolveVideo: (message: WorkbenchChatMessage) => void;
  onSuperResolveImage: (message: WorkbenchChatMessage) => void;
  onOpenResultInCanvas?: (payload: WorkbenchResultActionPayload) => void;
  downloadFilenameBase?: string;
}) {
  const [mediaRatio, setMediaRatio] = useState<string | null>(null);
  const [actionNotice, setActionNotice] = useState<string | null>(null);
  const [isPublishing, setIsPublishing] = useState(false);
  const [isDownloading, setIsDownloading] = useState(false);
  const [mediaExpired, setMediaExpired] = useState(false);
  const isVideo = message.resultType === "video";
  const hasMedia = !!message.resultUrl;
  const isSuperResolving =
    hasMedia &&
    message.status === "thinking" &&
    message.taskStatusLabel?.includes("超分");
  const promptText = message.prompt || (message.resultUrl ? "" : message.body);

  const handleCopyPrompt = () => {
    if (promptText) {
      navigator.clipboard.writeText(promptText);
    }
  };

  const specs = message.result?.specs || [];
  const modelTag = cleanModelDisplayLabel(specs[0] || "");
  const modeTag = specs.length > 1 ? specs[1] : "";
  const mediaStyle = mediaRatio ? ({ "--media-ratio": mediaRatio } as CSSProperties) : undefined;
  const durationTag = specs.find((spec) => /\b\d+s\b|秒/.test(spec)) || "5s";
  const openGeneratedMedia = () => {
    if (!message.resultUrl) return;
    onOpenMedia({
      kind: isVideo ? "video" : "image",
      name: message.result?.title || (isVideo ? "生成视频" : "生成图片"),
      token: message.taskId ? `#${message.taskId}` : "@结果",
      previewUrl: message.resultUrl,
      remoteUrl: message.resultUrl,
    });
  };
  const resultTitle = message.result?.title || (isVideo ? "生成视频" : "生成图片");

  const handleDownload = async () => {
    if (!message.resultUrl || isDownloading) return;
    setIsDownloading(true);
    setActionNotice("正在准备下载...");
    try {
      const filenameBase = downloadFilenameBase || resultTitle;
      const status = await downloadResultAsset(message.resultUrl, filenameBase, isVideo, message.resultOssKey ? undefined : message.taskId);
      setActionNotice(status === "saved" ? "已保存到本地" : "已开始保存到本地");
    } catch (error) {
      if (error instanceof DOMException && error.name === "AbortError") {
        setActionNotice("已取消保存");
      } else {
        setActionNotice(error instanceof Error ? error.message : "下载失败，请稍后重试");
      }
    } finally {
      setIsDownloading(false);
    }
  };

  const buildResultPayload = (): WorkbenchResultActionPayload | null => {
    if (!message.resultUrl || !message.resultType) return null;
    return {
      title: resultTitle,
      prompt: promptText,
      resultUrl: message.resultUrl,
      resultType: message.resultType,
      taskId: message.taskId,
      resultOriginalUrl: message.resultOriginalUrl,
      resultOssKey: message.resultOssKey,
      resultMimeType: message.resultMimeType,
    };
  };

  const handleSaveAsset = async () => {
    const payload = buildResultPayload();
    if (!payload) return;
    saveAssetToLocalLibrary({
      type: payload.resultType === "video" ? "video" : "scene",
      name: payload.title,
      description: payload.prompt || "从生成结果保存的素材。",
      imageUrl: payload.resultUrl,
      tags: [payload.resultType === "video" ? "生成视频" : "生成图片", "工作台"],
    });
    try {
      await assetClient.create({
        type: payload.resultType === "video" ? "video" : "image",
        name: payload.title,
        description: payload.prompt || "从生成结果保存的素材。",
        url: payload.resultUrl,
        ossKey: payload.resultOssKey,
        tags: [payload.resultType === "video" ? "生成视频" : "生成图片", "工作台"],
        sourceTaskId: payload.taskId,
        metadata: {
          source: "workbench-result",
          prompt: payload.prompt,
          resultType: payload.resultType,
          originalUrl: payload.resultOriginalUrl || null,
          mimeType: payload.resultMimeType || null,
        },
      });
      setActionNotice("已保存到服务器资产库");
    } catch {
      setActionNotice("已保存到本地资产库，登录后可同步服务器。");
    }
  };

  const handleContinueInCanvas = () => {
    const payload = buildResultPayload();
    if (!payload) return;
    onOpenResultInCanvas?.(payload);
    setActionNotice("已打开画布继续编辑");
  };

  const handlePublish = async () => {
    const payload = buildResultPayload();
    if (!payload || isPublishing) return;
    setIsPublishing(true);
    setActionNotice(null);
    try {
      await communityClient.publishCase({
        title: payload.title,
        description: payload.prompt || "从工作台生成结果提交的社区作品。",
        coverUrl: payload.resultUrl,
        tags: [payload.resultType === "video" ? "视频" : "图片", "Web生成"],
        metadata: {
          source: "workbench-result",
          taskId: payload.taskId,
          prompt: payload.prompt,
          resultType: payload.resultType,
          resultOssKey: payload.resultOssKey || null,
          resultOriginalUrl: payload.resultOriginalUrl || null,
          resultMimeType: payload.resultMimeType || null,
        },
        assets: [
          {
            assetType: payload.resultType,
            title: payload.title,
            url: payload.resultUrl,
            ossKey: payload.resultOssKey,
            metadata: {
              prompt: payload.prompt,
              taskId: payload.taskId,
              originalUrl: payload.resultOriginalUrl || null,
              mimeType: payload.resultMimeType || null,
            },
          },
        ],
      });
      setActionNotice("已提交社区审核。");
    } catch (error) {
      setActionNotice(error instanceof Error ? error.message : "提交社区失败");
    } finally {
      setIsPublishing(false);
    }
  };

  return (
    <div className={`ai-chat-image-result-card result-card-enter${isVideo ? " is-video" : ""}`}>
      {hasMedia ? (
        <div className={`ai-chat-image-frame${isVideo ? " ai-chat-image-frame--video" : ""}${mediaExpired ? " is-expired" : ""}`} style={mediaStyle}>
          {mediaExpired ? (
            <div className="ai-chat-media-expired">
              <span>资源已过期</span>
              <button type="button" onClick={() => onRegenerate(message)}>
                <ReloadOutlined /> 重新生成
              </button>
            </div>
          ) : isVideo ? (
            <button type="button" className="ai-chat-video-poster" onClick={openGeneratedMedia}>
              <video
                src={message.resultUrl}
                playsInline
                muted
                loop
                preload="metadata"
                onError={() => setMediaExpired(true)}
              />
              <span className="ai-chat-video-poster__scrim" />
              <span className="ai-chat-video-poster__play">
                <CaretRightOutlined />
              </span>
              <span className="ai-chat-video-poster__duration">
                <ClockCircleOutlined /> {durationTag}
              </span>
            </button>
          ) : (
            <button type="button" className="ai-chat-image-preview-button" onClick={openGeneratedMedia}>
              <img
                src={message.resultUrl}
                alt="生成结果"
                onError={() => setMediaExpired(true)}
                onLoad={(event) => {
                  const { naturalWidth, naturalHeight } = event.currentTarget;
                  if (naturalWidth > 0 && naturalHeight > 0) {
                    setMediaRatio(`${naturalWidth} / ${naturalHeight}`);
                  }
                }}
              />
            </button>
          )}
        </div>
      ) : null}

      <div className="ai-chat-image-result-meta">
        <div className="ai-chat-image-result-meta__top">
          <strong>{isVideo ? "视频已生成" : "图像已生成"}</strong>
          <span>{message.taskStatusLabel || "Completed"}</span>
        </div>
        {promptText && <p className="ai-chat-image-result-meta__prompt">{promptText}</p>}
        <div className="ai-chat-image-result-meta__chips">
          {modelTag && <span className="ai-chat-chip">{modelTag}</span>}
          {modeTag && <span className="ai-chat-chip">{modeTag}</span>}
        </div>
        {isSuperResolving ? (
          <div className="ai-chat-image-result-progress" aria-label="视频超分进度">
            <SmoothedProgressBar
              progress={message.taskProgress ?? 18}
              status={message.status || "thinking"}
              label={message.taskStatusLabel || "超分处理中..."}
              progressSource="estimated"
              expectedDurationMs={DEFAULT_SUPER_RESOLUTION_EXPECTED_DURATION_MS}
            />
          </div>
        ) : null}
      </div>

      <div className="ai-chat-image-actions">
        <button
          type="button"
          className="ai-chat-image-actions__primary"
          onClick={() => onRegenerate(message)}
        >
          <ReloadOutlined /> 重新生成
        </button>
        <span className="ai-chat-image-actions__spacer" />
        <button
          type="button"
          className="ai-chat-image-actions__icon"
          onClick={() => void handleDownload()}
          disabled={isDownloading}
          title={isDownloading ? "正在准备下载" : "下载到本地"}
          aria-label={isDownloading ? "正在准备下载" : "下载到本地"}
        >
          <DownloadOutlined />
        </button>
        {isVideo && message.resultUrl ? (
          <button
            type="button"
            className="ai-chat-image-actions__icon ai-chat-image-actions__icon--super-resolution"
            onClick={() => onSuperResolveVideo(message)}
            disabled={isSuperResolving}
            title="超分"
            aria-label="超分"
          >
            <ThunderboltOutlined />
          </button>
        ) : null}
        {!isVideo && message.resultUrl ? (
          <button
            type="button"
            className="ai-chat-image-actions__icon ai-chat-image-actions__icon--super-resolution"
            onClick={() => onSuperResolveImage(message)}
            disabled={isSuperResolving}
            title="超分"
            aria-label="超分"
          >
            <ThunderboltOutlined />
          </button>
        ) : null}
        <button type="button" className="ai-chat-image-actions__icon" onClick={handleCopyPrompt}>
          <CopyOutlined />
        </button>
      </div>
      {hasMedia ? (
        <div className="ai-chat-result-flow">
          <button type="button" onClick={() => void handleSaveAsset()}>
            <AppstoreOutlined /> 存资产
          </button>
          <button type="button" onClick={handleContinueInCanvas} disabled={!onOpenResultInCanvas}>
            <PictureOutlined /> 进画布
          </button>
          <button type="button" onClick={() => void handlePublish()} disabled={isPublishing}>
            <ThunderboltOutlined /> {isPublishing ? "提交中..." : "发社区"}
          </button>
          {actionNotice ? <span>{actionNotice}</span> : null}
        </div>
      ) : null}
    </div>
  );
});

// ─── ChatAttachmentPreview ────────────────────────────────────────────────────

export const ChatAttachmentPreview = memo(function ChatAttachmentPreview({
  item,
  onOpen,
}: {
  item: WorkbenchChatAttachment;
  onOpen: (item: WorkbenchChatAttachment) => void;
}) {
  const [mediaFailed, setMediaFailed] = useState(false);
  const urlExpired = !item.previewUrl || item.previewUrl.startsWith("blob:");
  const hasMedia = !urlExpired && !mediaFailed && (item.kind === "image" || item.kind === "video");

  return (
    <button
      type="button"
      className={`ai-chat-attachment-thumb${!hasMedia ? " is-file" : ""}`}
      onClick={() => hasMedia && onOpen(item)}
      disabled={!hasMedia}
      title={`${item.token} ${item.name}`}
    >
      <span className="ai-chat-attachment-thumb__media">
        {hasMedia && item.kind === "image" ? (
          <img src={item.previewUrl} alt={item.name} loading="lazy" onError={() => setMediaFailed(true)} />
        ) : hasMedia && item.kind === "video" ? (
          <video src={item.previewUrl} muted playsInline onError={() => setMediaFailed(true)} />
        ) : (
          <span className="ai-chat-attachment-thumb__icon">
            {item.kind === "audio" ? <SoundOutlined /> : <FileTextOutlined />}
          </span>
        )}
      </span>
      <span className="ai-chat-attachment-thumb__label">{item.token}</span>
    </button>
  );
});

// ─── GenerationPendingCard ────────────────────────────────────────────────────

export const GenerationPendingCard = memo(function GenerationPendingCard({
  message,
  onStop,
}: {
  message: WorkbenchChatMessage;
  onStop?: () => void;
}) {
  const specs = message.result?.specs || [];
  const prompt = message.prompt || message.body;
  const isVideo = message.mode === "video";
  const expectedDurationMs = isVideo
    ? DEFAULT_VIDEO_GENERATION_EXPECTED_DURATION_MS
    : DEFAULT_IMAGE_GENERATION_EXPECTED_DURATION_MS;
  const progressStatus = message.status === "thinking" ? "running" : "completed";
  const smoothed = useSmoothedProgress(message.taskProgress ?? 5, progressStatus, {
    progressSource: progressStatus === "running" ? "estimated" : "real",
    startedAt: message.createdAt,
    expectedDurationMs,
  });
  const remainingLabel = progressStatus === "running"
    ? formatEstimatedRemainingLabel({
        nowMs: Date.now(),
        startedAt: message.createdAt,
        expectedDurationMs,
      })
    : null;
  const statusLabel = message.taskStatusLabel || (isVideo ? "视频生成中" : "图片生成中");

  return (
    <div className={`ai-generation-pending-card${isVideo ? " is-video" : " is-image"}`}>
      <div className="ai-generation-pending-card__stage">
        <div className="ai-generation-pending-card__grid" />
        <div className="ai-generation-pending-card__glow" />
        <div className="ai-generation-pending-card__loader">
          <span className="ai-generation-pending-card__ring" />
          <em className="ai-generation-pending-card__percent">{smoothed}%</em>
        </div>
        <div className="ai-generation-pending-card__scan" />
      </div>
      <div className="ai-generation-pending-card__meta">
        <div>
          <strong>{remainingLabel ? `${statusLabel} / ${remainingLabel}` : statusLabel}</strong>
          <span>{prompt}</span>
        </div>
        {specs.length > 0 && (
          <div className="ai-generation-pending-card__chips">
            {specs.slice(0, 3).map((spec) => (
              <span key={spec}>{spec}</span>
            ))}
          </div>
        )}
        {onStop && (
          <button type="button" className="ai-generation-pending-card__stop" onClick={onStop} title="终止任务">
            <StopOutlined /> 终止
          </button>
        )}
      </div>
    </div>
  );
});
