import { useEffect, useMemo, useRef, useState } from "react";
import { Alert, Button, Empty, Input, Result, Segmented, Skeleton, Tabs, Tag, message } from "antd";
import { useParams, useSearchParams } from "react-router-dom";
import {
  AudioOutlined,
  CalendarOutlined,
  CaretRightFilled,
  ClockCircleOutlined,
  CopyOutlined,
  FastForwardOutlined,
  FileTextOutlined,
  LockOutlined,
  PauseOutlined,
  RobotOutlined,
  ShareAltOutlined,
  TeamOutlined,
  UserOutlined,
} from "@ant-design/icons";
import dayjs from "dayjs";
import ReactMarkdown from "react-markdown";
import {
  getMeetingPreviewAccess,
  getPublicMeetingPreview,
  type MeetingTranscriptVO,
  type MeetingVO,
} from "../../api/business/meeting";
import { buildMeetingAnalysis } from "./meetingAnalysis";
import "./MeetingPreview.css";

type AnalysisTab = "chapters" | "speakers" | "actions" | "todos";
type PreviewPageTab = "summary" | "transcript";

const TEXT = {
  statusTranscribing: "\u8f6c\u5199\u4e2d",
  statusSummarizing: "\u603b\u7ed3\u4e2d",
  statusCompleted: "\u5df2\u5b8c\u6210",
  statusPending: "\u5f85\u5904\u7406",
  hintTranscribing: "\u4f1a\u8bae\u5185\u5bb9\u4ecd\u5728\u6574\u7406\u4e2d\uff0c\u9884\u89c8\u4f1a\u6301\u7eed\u8865\u5168\u3002",
  hintSummarizing: "AI \u6b63\u5728\u751f\u6210\u4f1a\u8bae\u603b\u7ed3\uff0c\u5df2\u5b8c\u6210\u5185\u5bb9\u4f1a\u4f18\u5148\u5c55\u793a\u3002",
  hintCompleted: "\u4f1a\u8bae\u7eaa\u8981\u3001\u5206\u6790\u548c\u8f6c\u5f55\u5185\u5bb9\u5df2\u751f\u6210\u5b8c\u6210\u3002",
  hintPending: "\u5f53\u524d\u4f1a\u8bae\u5c1a\u672a\u751f\u6210\u5b8c\u6574\u5185\u5bb9\uff0c\u8bf7\u7a0d\u540e\u91cd\u8bd5\u3002",
  missingMeetingId: "\u672a\u63d0\u4f9b\u4f1a\u8bae\u7f16\u53f7",
  loadFailed: "\u4f1a\u8bae\u9884\u89c8\u52a0\u8f7d\u5931\u8d25",
  noMeetingData: "\u672a\u627e\u5230\u4f1a\u8bae\u6570\u636e",
  previewLabel: "\u4f1a\u8bae\u9884\u89c8",
  untitledMeeting: "\u672a\u547d\u540d\u4f1a\u8bae",
  meetingTime: "\u4f1a\u8bae\u65f6\u95f4",
  hostCreator: "\u4e3b\u6301/\u521b\u5efa",
  participantsCount: "\u53c2\u4f1a\u4eba\u6570",
  tagsCount: "\u6807\u7b7e\u6570\u91cf",
  notSet: "\u672a\u8bbe\u7f6e",
  notFilled: "\u672a\u586b\u5199",
  pageSummary: "\u603b\u7ed3\u4e0e\u5206\u6790",
  pageTranscript: "\u8f6c\u5f55\u4e0e\u97f3\u9891",
  copyLink: "\u590d\u5236\u94fe\u63a5",
  shareNow: "\u7acb\u5373\u5206\u4eab",
  shareCopied: "\u9884\u89c8\u94fe\u63a5\u5df2\u590d\u5236",
  shareFallbackCopied: "\u5f53\u524d\u8bbe\u5907\u4e0d\u652f\u6301\u7cfb\u7edf\u5206\u4eab\uff0c\u5df2\u4e3a\u4f60\u590d\u5236\u94fe\u63a5",
  shareFailed: "\u5206\u4eab\u5931\u8d25\uff0c\u8bf7\u5148\u590d\u5236\u94fe\u63a5",
  accessCheck: "\u8bbf\u95ee\u6821\u9a8c",
  passwordRequired: "\u8be5\u4f1a\u8bae\u9700\u8981\u8bbf\u95ee\u5bc6\u7801",
  passwordHint: "\u8bf7\u8f93\u5165\u4f1a\u8bae\u7684 access_password \u540e\u7ee7\u7eed\u8bbf\u95ee\u9884\u89c8\u5185\u5bb9\u3002",
  passwordPlaceholder: "\u8bf7\u8f93\u5165 access_password",
  openPreview: "\u8fdb\u5165\u9884\u89c8",
  invalidPassword: "\u8bbf\u95ee\u5bc6\u7801\u9519\u8bef",
  basicInfo: "\u57fa\u672c\u4fe1\u606f",
  meetingOverview: "\u4f1a\u8bae\u6982\u51b5",
  creator: "\u521b\u5efa\u4eba",
  host: "\u4e3b\u6301\u4eba",
  createdAt: "\u521b\u5efa\u65f6\u95f4",
  audioStatus: "\u97f3\u9891\u72b6\u6001",
  participants: "\u53c2\u4f1a\u4eba\u5458",
  tags: "\u4f1a\u8bae\u6807\u7b7e",
  aiAnalysis: "AI \u5206\u6790",
  analysis: "\u4f1a\u8bae\u5206\u6790",
  previewExtra: "\u9884\u89c8\u9875\u4ec5\u8bfb\u5c55\u793a",
  audioPlaybackWarning: "\u97f3\u9891\u4fdd\u5b58\u5931\u8d25\uff0c\u53ef\u80fd\u5f71\u54cd\u56de\u653e\u3002",
  summaryOverview: "\u5168\u6587\u6982\u8981",
  summaryEmpty: "\u6682\u65e0\u6982\u8981\u5185\u5bb9",
  analysisChapters: "\u7ae0\u8282",
  analysisSpeakers: "\u53d1\u8a00\u4eba",
  analysisKeyPoints: "\u5173\u952e\u8981\u70b9",
  analysisTodos: "\u5f85\u529e\u4e8b\u9879",
  noChapterAnalysis: "\u6682\u65e0\u7ae0\u8282\u5206\u6790",
  noSpeakerAnalysis: "\u6682\u65e0\u53d1\u8a00\u4eba\u5206\u6790",
  noKeyPoints: "\u6682\u65e0\u5173\u952e\u8981\u70b9",
  noTodos: "\u6682\u65e0\u5f85\u529e\u4e8b\u9879",
  chapterFallback: "\u7ae0\u8282",
  speakerFallback: "\u53d1\u8a00\u4eba",
  speakerSummary: "\u53d1\u8a00\u6982\u8ff0",
  keyPointFallback: "\u8981\u70b9",
  noChapterSummary: "\u6682\u65e0\u7ae0\u8282\u63cf\u8ff0",
  noSpeakerSummary: "\u6682\u65e0\u53d1\u8a00\u603b\u7ed3",
  noKeyPointSummary: "\u6682\u65e0\u8981\u70b9\u8bf4\u660e",
  summarySection: "\u4f1a\u8bae\u7eaa\u8981",
  fullSummary: "\u5b8c\u6574\u7eaa\u8981",
  noSummary: "\u6682\u65e0\u4f1a\u8bae\u7eaa\u8981",
  transcriptSection: "\u4f1a\u8bae\u8f6c\u5f55",
  transcriptTitle: "\u9010\u6bb5\u8f6c\u5f55",
  noDuration: "\u6682\u65e0\u65f6\u957f",
  audioUnavailable: "\u97f3\u9891\u6587\u4ef6\u4e0d\u53ef\u7528\uff0c\u4ec5\u5c55\u793a\u8f6c\u5f55\u5185\u5bb9\u3002",
  noTranscript: "\u6682\u65e0\u8f6c\u5f55\u5185\u5bb9",
  unknownSpeaker: "\u672a\u77e5\u53d1\u8a00\u4eba",
  disclaimer: "智能内容由用户会议内容 + AI 模型生成，我们不对内容准确性和完整性做任何保证，亦不代表我们的观点或态度",
  shareText: "\u6211\u5411\u4f60\u5206\u4eab\u4e86\u4e00\u4e2a\u4f1a\u8bae\u9884\u89c8\u94fe\u63a5",
  audioSaved: "\u5df2\u4fdd\u5b58",
  audioSaveFailed: "\u4fdd\u5b58\u5931\u8d25",
  audioUploaded: "\u5df2\u4e0a\u4f20",
  audioNotSaved: "\u672a\u4fdd\u5b58",
};

const STATUS_META: Record<number, { label: string; className: string; hint: string }> = {
  1: { label: TEXT.statusTranscribing, className: "is-processing", hint: TEXT.hintTranscribing },
  2: { label: TEXT.statusSummarizing, className: "is-processing", hint: TEXT.hintSummarizing },
  3: { label: TEXT.statusCompleted, className: "is-complete", hint: TEXT.hintCompleted },
};

function formatDurationRange(startTime?: number, endTime?: number) {
  const format = (milliseconds?: number) => {
    const safeMs = Math.max(0, milliseconds || 0);
    const totalSeconds = Math.floor(safeMs / 1000);
    const minutes = Math.floor(totalSeconds / 60);
    const seconds = totalSeconds % 60;
    return `${minutes.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`;
  };

  return `${format(startTime)} - ${format(endTime)}`;
}

function splitDisplayItems(value?: string) {
  return (value || "")
    .split(",")
    .map((item) => item.trim())
    .filter(Boolean);
}

function transcriptColorSeed(speakerKey: string) {
  const palette = ["#315f8b", "#b86432", "#557a46", "#6d4fa7", "#a33f57", "#0f766e"];
  const score = Array.from(speakerKey).reduce((sum, char) => sum + char.charCodeAt(0), 0);
  return palette[score % palette.length];
}

async function copyText(text: string) {
  if (navigator.clipboard?.writeText) {
    await navigator.clipboard.writeText(text);
    return;
  }

  const textarea = document.createElement("textarea");
  textarea.value = text;
  textarea.setAttribute("readonly", "true");
  textarea.style.position = "fixed";
  textarea.style.opacity = "0";
  document.body.appendChild(textarea);
  textarea.select();
  document.execCommand("copy");
  document.body.removeChild(textarea);
}

export default function MeetingPreview() {
  const { id } = useParams();
  const [searchParams] = useSearchParams();
  const audioRef = useRef<HTMLAudioElement | null>(null);
  const transcriptItemRefs = useRef<Record<number, HTMLDivElement | null>>({});
  const [meeting, setMeeting] = useState<MeetingVO | null>(null);
  const [transcripts, setTranscripts] = useState<MeetingTranscriptVO[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState("");
  const [analysisTab, setAnalysisTab] = useState<AnalysisTab>("chapters");
  const [pageTab, setPageTab] = useState<PreviewPageTab>("summary");
  const [activeTranscriptId, setActiveTranscriptId] = useState<number | null>(null);
  const [passwordRequired, setPasswordRequired] = useState(false);
  const [passwordVerified, setPasswordVerified] = useState(false);
  const [accessPassword, setAccessPassword] = useState("");
  const [passwordError, setPasswordError] = useState("");
  const [audioPlaying, setAudioPlaying] = useState(false);
  const [audioCurrentTime, setAudioCurrentTime] = useState(0);
  const [audioDuration, setAudioDuration] = useState(0);
  const [audioPlaybackRate, setAudioPlaybackRate] = useState(1);
  const [isMobile, setIsMobile] = useState(() =>
    typeof window !== "undefined" ? window.matchMedia("(max-width: 767px)").matches : false,
  );
  const presetAccessPassword = useMemo(() => (searchParams.get("accessPassword") || "").trim(), [searchParams]);

  useEffect(() => {
    let mounted = true;

    const load = async () => {
      if (!id) {
        setError(TEXT.missingMeetingId);
        setLoading(false);
        return;
      }

      setLoading(true);
      setError("");
        setMeeting(null);
        setTranscripts([]);
        setPasswordRequired(false);
        setPasswordVerified(false);
        setAccessPassword(presetAccessPassword);
        setPasswordError("");

        try {
          const meetingId = Number(id);
          const accessRes = await getMeetingPreviewAccess(meetingId);
        if (!mounted) {
          return;
        }

          const requiresPassword = !!accessRes.data.data.passwordRequired;
          setPasswordRequired(requiresPassword);
          if (requiresPassword) {
            if (!presetAccessPassword) {
              setLoading(false);
              return;
            }
            try {
              const previewRes = await getPublicMeetingPreview(meetingId, presetAccessPassword);
              if (!mounted) {
                return;
              }
              setMeeting(previewRes.data.data.meeting);
              setTranscripts(previewRes.data.data.transcripts || []);
              setPasswordVerified(true);
              return;
            } catch (requestError: any) {
              if (!mounted) {
                return;
              }
              setPasswordError(requestError?.response?.data?.msg || requestError?.msg || TEXT.invalidPassword);
              setPasswordVerified(false);
              setLoading(false);
              return;
            }
          }

          const previewRes = await getPublicMeetingPreview(meetingId);
        if (!mounted) {
          return;
        }

        setMeeting(previewRes.data.data.meeting);
        setTranscripts(previewRes.data.data.transcripts || []);
        setPasswordVerified(true);
      } catch (requestError: any) {
        if (!mounted) {
          return;
        }

        setError(requestError?.response?.data?.msg || requestError?.msg || TEXT.loadFailed);
      } finally {
        if (mounted) {
          setLoading(false);
        }
      }
    };

    load();

    return () => {
      mounted = false;
    };
  }, [id, presetAccessPassword]);

  useEffect(() => {
    if (typeof window === "undefined") {
      return;
    }

    const mediaQuery = window.matchMedia("(max-width: 767px)");
    const handleChange = (event: MediaQueryListEvent) => {
      setIsMobile(event.matches);
    };

    setIsMobile(mediaQuery.matches);
    mediaQuery.addEventListener("change", handleChange);
    return () => {
      mediaQuery.removeEventListener("change", handleChange);
    };
  }, []);

  const analysis = useMemo(
    () => buildMeetingAnalysis(meeting?.analysis, meeting?.summaryContent, meeting?.tags || ""),
    [meeting?.analysis, meeting?.summaryContent, meeting?.tags],
  );

  const participants = useMemo(() => splitDisplayItems(meeting?.participants), [meeting?.participants]);
  const transcriptSpeakers = useMemo(() => {
    const speakers = transcripts
      .map((item) => item.speakerName || item.speakerLabel || item.speakerId || "")
      .map((item) => item.trim())
      .filter(Boolean);
    return Array.from(new Set(speakers));
  }, [transcripts]);
  const tags = useMemo(() => splitDisplayItems(meeting?.tags), [meeting?.tags]);
  const keywords = useMemo(() => analysis.keywords || [], [analysis.keywords]);
  const statusMeta = STATUS_META[meeting?.status || 0] || {
    label: TEXT.statusPending,
    className: "is-warning",
    hint: TEXT.hintPending,
  };
  const audioStatusLabel = useMemo(() => {
    if (meeting?.audioSaveStatus === "SUCCESS") {
      return TEXT.audioSaved;
    }
    if (meeting?.audioSaveStatus === "FAILED") {
      return TEXT.audioSaveFailed;
    }
    if (meeting?.audioUrl) {
      return TEXT.audioUploaded;
    }
    return TEXT.audioNotSaved;
  }, [meeting?.audioSaveStatus, meeting?.audioUrl]);
  const shareUrl = typeof window !== "undefined" ? window.location.href : "";
  const participantCountValue =
    isMobile && transcriptSpeakers.length > 0 ? transcriptSpeakers.length : participants.length;

  const meetingDuration = useMemo(() => {
    if (transcripts.length > 0) {
      const last = transcripts[transcripts.length - 1];
      return last.endTime || 0;
    }
    return 0;
  }, [transcripts]);

  useEffect(() => {
    if (!activeTranscriptId) {
      return;
    }

    const target = transcriptItemRefs.current[activeTranscriptId];
    if (!target) {
      return;
    }

    target.scrollIntoView({ behavior: "smooth", block: "nearest" });
  }, [activeTranscriptId]);

  const handleTranscriptSeek = (item: MeetingTranscriptVO) => {
    if (!audioRef.current) {
      return;
    }

    audioRef.current.currentTime = Math.max(0, (item.startTime || 0) / 1000);
    audioRef.current.play().catch(() => {});
  };

  const toggleAudioPlayback = () => {
    if (!audioRef.current) return;
    if (audioPlaying) {
      audioRef.current.pause();
    } else {
      audioRef.current.play().catch(() => {});
    }
  };

  const handleAudioProgressChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    if (!audioRef.current) return;
    const time = parseFloat(e.target.value);
    audioRef.current.currentTime = time;
    setAudioCurrentTime(time);
  };

  const cyclePlaybackRate = () => {
    if (!audioRef.current) return;
    const nextRate = audioPlaybackRate === 1 ? 1.5 : audioPlaybackRate === 1.5 ? 2 : 1;
    audioRef.current.playbackRate = nextRate;
    setAudioPlaybackRate(nextRate);
  };

  const formatPlayerTime = (seconds: number) => {
    if (!seconds || isNaN(seconds)) return '00:00';
    const m = Math.floor(seconds / 60);
    const s = Math.floor(seconds % 60);
    return `${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;
  };

  const handleAudioTimeUpdate = () => {
    if (!audioRef.current) return;
    const currentSeconds = audioRef.current.currentTime;
    setAudioCurrentTime(currentSeconds);
    
    // Also update duration if it's available now
    if (audioRef.current.duration && audioDuration !== audioRef.current.duration) {
      setAudioDuration(audioRef.current.duration);
    }

    if (transcripts.length === 0) return;

    const currentMs = currentSeconds * 1000;
    const currentItem = transcripts.find(
      (item) => currentMs >= (item.startTime || 0) && currentMs <= (item.endTime || 0),
    );

    setActiveTranscriptId(currentItem?.id || null);
  };

  const handleAudioEnded = () => {
    setAudioPlaying(false);
  };

  const handleAudioPlay = () => setAudioPlaying(true);
  const handleAudioPause = () => setAudioPlaying(false);
  const handleAudioLoadedMetadata = () => {
    if (audioRef.current) {
      setAudioDuration(audioRef.current.duration);
    }
  };

  const renderMeetingTitle = (title?: string) => {
    const safeTitle = title || TEXT.untitledMeeting;
    return safeTitle.split(/(\d+)/).map((part, index) =>
      /\d+/.test(part) ? (
        <span key={`${part}-${index}`} className="meeting-preview-title-number">
          {part}
        </span>
      ) : (
        <span key={`${part}-${index}`}>{part}</span>
      ),
    );
  };

  const handlePasswordSubmit = async () => {
    if (!id) {
      return;
    }

    setLoading(true);
    setPasswordError("");
    try {
      const previewRes = await getPublicMeetingPreview(Number(id), accessPassword.trim());
      setMeeting(previewRes.data.data.meeting);
      setTranscripts(previewRes.data.data.transcripts || []);
      setPasswordVerified(true);
    } catch (requestError: any) {
      setPasswordError(requestError?.response?.data?.msg || requestError?.msg || TEXT.invalidPassword);
    } finally {
      setLoading(false);
    }
  };

  const handleCopyLink = async () => {
    try {
      await copyText(shareUrl);
      message.success(TEXT.shareCopied);
    } catch {
      message.error(TEXT.shareFailed);
    }
  };

  const handleShareNow = async () => {
    try {
      if (navigator.share) {
        await navigator.share({
          title: meeting?.title || TEXT.previewLabel,
          text: TEXT.shareText,
          url: shareUrl,
        });
        return;
      }

      await copyText(shareUrl);
      message.success(TEXT.shareFallbackCopied);
    } catch {
      message.error(TEXT.shareFailed);
    }
  };

  if (loading && (!passwordRequired || passwordVerified)) {
    return (
      <div className="meeting-preview-page">
        <div className="meeting-preview-shell meeting-preview-loading">
          <div className="meeting-preview-card meeting-preview-hero">
            <Skeleton active paragraph={{ rows: 4 }} />
          </div>
          <div className="meeting-preview-card meeting-preview-section">
            <Skeleton active paragraph={{ rows: 8 }} />
          </div>
          <div className="meeting-preview-card meeting-preview-section">
            <Skeleton active paragraph={{ rows: 10 }} />
          </div>
        </div>
      </div>
    );
  }

  if (passwordRequired && !passwordVerified) {
    return (
      <div className="meeting-preview-page">
        <div className="meeting-preview-shell meeting-preview-empty">
          <div className="meeting-preview-card meeting-preview-section meeting-preview-password-gate">
            <div className="meeting-preview-section-header">
              <div>
                <div className="meeting-preview-section-kicker">
                  <LockOutlined />
                  {TEXT.accessCheck}
                </div>
                <h2 className="meeting-preview-section-title">{TEXT.passwordRequired}</h2>
              </div>
            </div>

            <p className="meeting-preview-subtitle">{TEXT.passwordHint}</p>

            <div className="meeting-preview-password-form">
              <Input.Password
                value={accessPassword}
                placeholder={TEXT.passwordPlaceholder}
                onChange={(event) => setAccessPassword(event.target.value)}
                onPressEnter={handlePasswordSubmit}
              />
              <Button type="primary" onClick={handlePasswordSubmit} loading={loading} disabled={!accessPassword.trim()}>
                {TEXT.openPreview}
              </Button>
            </div>

            {passwordError ? <Alert type="error" showIcon message={passwordError} /> : null}
          </div>
        </div>
      </div>
    );
  }

  if (error) {
    return (
      <div className="meeting-preview-page">
        <div className="meeting-preview-shell meeting-preview-empty">
          <div className="meeting-preview-card meeting-preview-section">
            <Result status="error" title={TEXT.loadFailed} subTitle={error} />
          </div>
        </div>
      </div>
    );
  }

  if (!meeting) {
    return (
      <div className="meeting-preview-page">
        <div className="meeting-preview-shell meeting-preview-empty">
          <div className="meeting-preview-card meeting-preview-section">
            <Empty description={TEXT.noMeetingData} />
          </div>
        </div>
      </div>
    );
  }

  const summaryTab = (
    <div className="meeting-preview-tab-panel">
      <section className="meeting-preview-card meeting-preview-section">
        <div className="meeting-preview-section-header">
          <div>
            <div className="meeting-preview-section-kicker">
              <RobotOutlined />
              {TEXT.aiAnalysis}
            </div>
            <h2 className="meeting-preview-section-title">{TEXT.analysis}</h2>
          </div>
          <div className="meeting-preview-section-extra">{TEXT.previewExtra}</div>
        </div>

        {meeting.audioSaveStatus === "FAILED" ? (
          <Alert
            className="meeting-preview-alert"
            type="warning"
            showIcon
            message={meeting.audioSaveMessage || TEXT.audioPlaybackWarning}
          />
        ) : null}

        {keywords.length > 0 ? (
          <div className="meeting-preview-tags">
            {keywords.map((item) => (
              <span key={item} className="meeting-preview-tag">
                {item}
              </span>
            ))}
          </div>
        ) : null}

        <div className="meeting-preview-overview">
          <div className="meeting-preview-overview-label">{TEXT.summaryOverview}</div>
          <p className="meeting-preview-overview-copy">{analysis.overview || TEXT.summaryEmpty}</p>
        </div>

        <div className="meeting-preview-analysis-tabs">
          {[
            { label: TEXT.analysisChapters, value: "chapters" },
            { label: TEXT.analysisSpeakers, value: "speakers" },
            { label: TEXT.analysisKeyPoints, value: "actions" },
            { label: TEXT.analysisTodos, value: "todos" },
          ].map((tab) => (
            <div
              key={tab.value}
              className={`meeting-preview-analysis-tab ${analysisTab === tab.value ? 'active' : ''}`}
              onClick={() => setAnalysisTab(tab.value as AnalysisTab)}
            >
              {tab.label}
            </div>
          ))}
        </div>

        <div className="meeting-preview-analysis-panel">
          {analysisTab === "chapters" ? (
            analysis.chapters.length > 0 ? (
              analysis.chapters.map((item, index) => (
                <div className="meeting-preview-chapter" key={`${item.title || "chapter"}-${index}`}>
                  <div className="meeting-preview-chapter-time">{item.time || "--:--"}</div>
                  <div>
                    <strong className="meeting-preview-item-title">{item.title || `${TEXT.chapterFallback} ${index + 1}`}</strong>
                    <span className="meeting-preview-item-copy">{item.summary || TEXT.noChapterSummary}</span>
                  </div>
                </div>
              ))
            ) : (
              <div className="meeting-preview-list-empty">{TEXT.noChapterAnalysis}</div>
            )
          ) : null}

          {analysisTab === "speakers" ? (
            analysis.speakerSummaries.length > 0 ? (
              analysis.speakerSummaries.map((item, index) => (
                <div className="meeting-preview-speaker-card" key={`${item.speaker || "speaker"}-${index}`}>
                  <div className="meeting-preview-speaker-head">
                    <div className="meeting-preview-speaker-avatar">{(item.speaker || "S").slice(0, 1)}</div>
                    <div>
                      <div className="meeting-preview-speaker-name">{item.speaker || `${TEXT.speakerFallback} ${index + 1}`}</div>
                      <div className="meeting-preview-speaker-role">{TEXT.speakerSummary}</div>
                    </div>
                  </div>
                  <div className="meeting-preview-item-copy">{item.summary || TEXT.noSpeakerSummary}</div>
                </div>
              ))
            ) : (
              <div className="meeting-preview-list-empty">{TEXT.noSpeakerAnalysis}</div>
            )
          ) : null}

          {analysisTab === "actions" ? (
            analysis.keyPoints.length > 0 ? (
              analysis.keyPoints.map((item, index) => (
                <div className="meeting-preview-keypoint" key={`${item.title || "key-point"}-${index}`}>
                  <div className="meeting-preview-keypoint-index">{String(index + 1).padStart(2, "0")}</div>
                  <div>
                    <strong className="meeting-preview-item-title">{item.title || `${TEXT.keyPointFallback} ${index + 1}`}</strong>
                    <span className="meeting-preview-item-copy">{item.summary || TEXT.noKeyPointSummary}</span>
                    {(item.speaker || item.time) ? (
                      <div className="meeting-preview-item-meta">
                        {item.speaker ? <span className="meeting-preview-meta-pill">{item.speaker}</span> : null}
                        {item.time ? <span className="meeting-preview-meta-pill">{item.time}</span> : null}
                      </div>
                    ) : null}
                  </div>
                </div>
              ))
            ) : (
              <div className="meeting-preview-list-empty">{TEXT.noKeyPoints}</div>
            )
          ) : null}

          {analysisTab === "todos" ? (
            analysis.todos.length > 0 ? (
              analysis.todos.map((item, index) => (
                <div className="meeting-preview-todo" key={`${item}-${index}`}>
                  <span className="meeting-preview-todo-dot" />
                  <span className="meeting-preview-item-copy">{item}</span>
                </div>
              ))
            ) : (
              <div className="meeting-preview-list-empty">{TEXT.noTodos}</div>
            )
          ) : null}
        </div>
      </section>

      <section className="meeting-preview-card meeting-preview-section">
        <div className="meeting-preview-section-header">
          <div>
            <div className="meeting-preview-section-kicker">
              <FileTextOutlined />
              {TEXT.summarySection}
            </div>
            <h2 className="meeting-preview-section-title">{TEXT.fullSummary}</h2>
          </div>
        </div>

        <div className="meeting-preview-markdown">
          {meeting.summaryContent ? (
            <ReactMarkdown>{meeting.summaryContent}</ReactMarkdown>
          ) : (
            <Empty description={TEXT.noSummary} />
          )}
        </div>
      </section>
    </div>
  );

  const transcriptTab = (
    <div className="meeting-preview-tab-panel">
      <section className="meeting-preview-card meeting-preview-section">
        <div className="meeting-preview-section-header">
          <div>
            <div className="meeting-preview-section-kicker">
              <AudioOutlined />
              {TEXT.transcriptSection}
            </div>
            <h2 className="meeting-preview-section-title">{TEXT.transcriptTitle}</h2>
          </div>
          <div className="meeting-preview-section-extra">
            <ClockCircleOutlined style={{ marginRight: 6 }} />
            {meetingDuration > 0 ? formatDurationRange(0, meetingDuration) : TEXT.noDuration}
          </div>
        </div>

        {meeting.audioSaveStatus === "FAILED" ? (
          <Alert
            className="meeting-preview-alert"
            type="warning"
            showIcon
            message={meeting.audioSaveMessage || TEXT.audioUnavailable}
          />
        ) : null}

        <div className="meeting-preview-transcript-list">
          {transcripts.length > 0 ? (
            transcripts.map((item) => {
              const speakerKey = item.speakerName || item.speakerLabel || item.speakerId || "speaker";
              return (
                <div
                  key={item.id}
                  ref={(node) => {
                    transcriptItemRefs.current[item.id] = node;
                  }}
                  className={`meeting-preview-transcript-item ${activeTranscriptId === item.id ? "is-active" : ""}`}
                  onClick={() => handleTranscriptSeek(item)}
                >
                  <div className="meeting-preview-transcript-avatar">
                    {(speakerKey || "S").slice(0, 1)}
                  </div>
                  <div className="meeting-preview-transcript-content">
                    <div className="meeting-preview-transcript-meta">
                      <span className="meeting-preview-transcript-speaker">{speakerKey}</span>
                      <span className="meeting-preview-transcript-time">
                        {formatDurationRange(item.startTime, item.endTime)}
                      </span>
                    </div>
                    <div className="meeting-preview-transcript-text">
                      {item.content || TEXT.noTranscript}
                    </div>
                  </div>
                </div>
              );
            })
          ) : (
            <Empty description={TEXT.noTranscript} />
          )}
        </div>
      </section>
    </div>
  );

  return (
    <div className="meeting-preview-page">
      <div className="meeting-preview-shell">
        <section className="meeting-preview-card meeting-preview-hero">
          <div className="meeting-preview-eyebrow">
            <div className="meeting-preview-eyebrow-label">
              <FileTextOutlined />
              {TEXT.previewLabel}
            </div>
            <span className={`meeting-preview-status ${statusMeta.className}`}>{statusMeta.label}</span>
          </div>

          <div className="meeting-preview-hero-toolbar">
            <div>
              <h1 className="meeting-preview-title">{renderMeetingTitle(meeting.title)}</h1>
              <p className="meeting-preview-subtitle">{statusMeta.hint}</p>
            </div>
            <div className="meeting-preview-hero-actions">
              <Button icon={<CopyOutlined />} onClick={handleCopyLink}>
                {TEXT.copyLink}
              </Button>
              <Button type="primary" icon={<ShareAltOutlined />} onClick={handleShareNow}>
                {TEXT.shareNow}
              </Button>
            </div>
          </div>

          <div className="meeting-preview-metrics">
            <div className="meeting-preview-metric">
              <span className="meeting-preview-metric-label">{TEXT.meetingTime}</span>
              <span className="meeting-preview-metric-value">
                {meeting.meetingTime ? dayjs(meeting.meetingTime).format("YYYY.MM.DD HH:mm") : TEXT.notSet}
              </span>
            </div>
            <div className="meeting-preview-metric">
              <span className="meeting-preview-metric-label">{TEXT.createdAt}</span>
              <span className="meeting-preview-metric-value">
                {meeting.createdAt ? dayjs(meeting.createdAt).format("YYYY.MM.DD HH:mm") : TEXT.notSet}
              </span>
            </div>
            <div className="meeting-preview-metric">
              <span className="meeting-preview-metric-label">{TEXT.creator}</span>
              <span className="meeting-preview-metric-value">{meeting.creatorName || TEXT.notSet}</span>
            </div>
            <div className="meeting-preview-metric">
              <span className="meeting-preview-metric-label">{TEXT.host}</span>
              <span className="meeting-preview-metric-value">{meeting.hostName || TEXT.notSet}</span>
            </div>
            <div className="meeting-preview-metric">
              <span className="meeting-preview-metric-label">{TEXT.participantsCount}</span>
              <span className="meeting-preview-metric-value">{participantCountValue || TEXT.notFilled}</span>
            </div>
            <div className="meeting-preview-metric">
              <span className="meeting-preview-metric-label">会议时长</span>
              <span className="meeting-preview-metric-value">{meetingDuration > 0 ? formatDurationRange(0, meetingDuration).split(' - ')[1] : TEXT.noDuration}</span>
            </div>
          </div>

          {participants.length > 0 ? (
            <div className="meeting-preview-overview" style={{ marginTop: 20 }}>
              <div className="meeting-preview-overview-label">{TEXT.participants}</div>
              <div className="meeting-preview-tags">
                {participants.map((item) => (
                  <span key={item} className="meeting-preview-tag">
                    <TeamOutlined style={{ marginRight: 8 }} />
                    {item}
                  </span>
                ))}
              </div>
            </div>
          ) : null}

          {tags.length > 0 ? (
            <div className="meeting-preview-overview" style={{ marginTop: participants.length > 0 ? 12 : 20 }}>
              <div className="meeting-preview-overview-label">{TEXT.tags}</div>
              <div className="meeting-preview-tags">
                {tags.map((item) => (
                  <Tag key={item} bordered={false} className="meeting-preview-tag">
                    {item}
                  </Tag>
                ))}
              </div>
            </div>
          ) : null}
        </section>

        <div className="meeting-preview-panels">
          <section className="meeting-preview-card meeting-preview-section">
            <Tabs
              className="meeting-preview-page-tabs"
              activeKey={pageTab}
              onChange={(key) => setPageTab(key as PreviewPageTab)}
              items={[
                { key: "summary", label: TEXT.pageSummary, children: summaryTab },
                { key: "transcript", label: TEXT.pageTranscript, children: transcriptTab },
              ]}
            />
          </section>
        </div>

        <div className="meeting-preview-disclaimer">
          <RobotOutlined style={{ marginRight: 8 }} />
          {TEXT.disclaimer}
        </div>
      </div>

      {meeting.audioUrl && (
        <audio
          ref={audioRef}
          src={meeting.audioUrl}
          onTimeUpdate={handleAudioTimeUpdate}
          onPlay={handleAudioPlay}
          onPause={handleAudioPause}
          onEnded={handleAudioEnded}
          onLoadedMetadata={handleAudioLoadedMetadata}
          style={{ display: 'none' }}
          preload="metadata"
        />
      )}

      {meeting.audioUrl && pageTab === 'transcript' ? (
        <>
          <div style={{ height: 100, flexShrink: 0, pointerEvents: 'none' }} />
          <div className="transcript-player">
            <button type="button" className="player-main-btn" onClick={toggleAudioPlayback} aria-label="toggle-audio">
              {audioPlaying ? <PauseOutlined /> : <CaretRightFilled />}
            </button>
            <div className="player-progress-shell">
              <div className="player-time-row">
                <span>{formatPlayerTime(audioCurrentTime)}</span>
                <span>{formatPlayerTime(audioDuration)}</span>
              </div>
              <input
                className="player-range"
                type="range"
                min={0}
                max={audioDuration || 0}
                step={0.1}
                value={Math.min(audioCurrentTime, audioDuration || 0)}
                onChange={handleAudioProgressChange}
                style={{ backgroundSize: `${audioDuration ? (audioCurrentTime / audioDuration) * 100 : 0}% 100%` }}
              />
            </div>
            <button type="button" className="player-ghost-btn" onClick={cyclePlaybackRate}>
              <FastForwardOutlined />
              {audioPlaybackRate}x
            </button>
          </div>
        </>
      ) : null}
    </div>
  );
}
