import { useRef, useState, type ChangeEvent } from "react";
import { useNavigate, useParams } from "react-router";
import {
  ArrowRight,
  Check,
  Edit2,
  ImageIcon,
  Loader2,
  MapPin,
  Plus,
  RefreshCw,
  Sparkles,
  Trash2,
  Upload,
  Users,
  X,
  Box,
} from "lucide-react";
import {
  useCharacters,
  useCharacterStates,
  useDeleteCharacter,
  useDeleteCharacterState,
  useExtractCharacters,
  useGenerateCharacterImage,
  useGenerateCharacterStateImage,
  useSaveCharacter,
  useSaveCharacterState,
  useUploadCharacterImage,
} from "../../hooks/useAi";
import type { Character, CharacterState } from "../../lib/api/ai";
import { ImagePreviewModal } from "../components/ImagePreviewModal";

type SettingsTab = "characters" | "scenes" | "props";
type CharacterViewType = "front" | "side" | "back";
type ImageState = Record<CharacterViewType, string | null>;
type PendingFiles = Record<CharacterViewType, File | null>;
type CharacterStateDraft = Pick<CharacterState, "name" | "stateType"> & Partial<Pick<CharacterState, "description" | "prompt">>;

const ROLE_TYPES = [
  { value: "female_lead", label: "女主角" },
  { value: "male_lead", label: "男主角" },
  { value: "antagonist", label: "反派" },
  { value: "supporting", label: "配角" },
];

const ROLE_TYPE_LABELS: Record<string, string> = {
  female_lead: "女主角",
  male_lead: "男主角",
  antagonist: "反派",
  villain: "反派",
  supporting: "配角",
};

const CHARACTER_VIEWS: Array<{ type: CharacterViewType; label: string; shortLabel: string }> = [
  { type: "front", label: "正面", shortLabel: "正" },
  { type: "side", label: "侧面", shortLabel: "侧" },
  { type: "back", label: "背面", shortLabel: "背" },
];

const emptyForm = (): Partial<Character> => ({
  name: "",
  roleType: "supporting",
  gender: "female",
  age: "",
  personality: "",
  costume: "",
  visualHint: "",
  imagePrompt: "",
});

const emptyImageState = (): ImageState => ({
  front: null,
  side: null,
  back: null,
});

const emptyPendingFiles = (): PendingFiles => ({
  front: null,
  side: null,
  back: null,
});

function revokeObjectPreview(url: string | null) {
  if (url?.startsWith("blob:")) {
    URL.revokeObjectURL(url);
  }
}

function revokeImagePreviews(previews: ImageState) {
  Object.values(previews).forEach(revokeObjectPreview);
}

const emptyStateDraft = (): CharacterStateDraft => ({
  name: "",
  stateType: "costume",
  description: "",
  prompt: "",
});

function getCharacterViewUrl(character: Partial<Character>, viewType: CharacterViewType) {
  if (viewType === "front") {
    return character.frontImageUrl ?? character.imageUrl ?? null;
  }
  if (viewType === "side") {
    return character.sideImageUrl ?? null;
  }
  return character.backImageUrl ?? null;
}

/** AI 生成的角色三视图是单张拼图，存在 imageUrl 上 */
function getCharacterSheetUrl(character: Partial<Character>): string | null {
  return character.imageUrl ?? null;
}

function hasCharacterImage(character: Partial<Character>) {
  if (getCharacterSheetUrl(character)) return true;
  return CHARACTER_VIEWS.every((view) => !!getCharacterViewUrl(character, view.type));
}

function isMostlyEnglishPrompt(prompt: string) {
  if (!prompt.trim()) return false;
  const latinChars = prompt.match(/[A-Za-z]/g)?.length ?? 0;
  const chineseChars = prompt.match(/[\u4e00-\u9fff]/g)?.length ?? 0;
  return latinChars > chineseChars * 2;
}

function buildChineseCharacterPrompt(character: Partial<Character>) {
  const name = character.name?.trim() || "该角色";
  const role = character.roleType ? ROLE_TYPE_LABELS[character.roleType] ?? character.roleType : "";
  const gender = character.gender === "male" ? "男性" : character.gender === "female" ? "女性" : character.gender;
  const details = [
    `角色：${name}`,
    role ? `定位：${role}` : "",
    gender ? `性别：${gender}` : "",
    character.age ? `年龄：${character.age}` : "",
    character.personality ? `性格气质：${character.personality}` : "",
    character.visualHint ? `外貌特征：${character.visualHint}` : "",
    character.costume ? `服装造型：${character.costume}` : "",
  ].filter(Boolean);

  return [
    "生成一张单张角色参考图，用于 AI 漫剧角色一致性。",
    details.join("；"),
    "画面需要同时包含清晰大头照，以及正面、侧面、背面全身三视图；五官、发型、服装、体型在所有视角中保持一致。",
    "全身视图自然站姿，双臂自然下垂，脚部完整入镜，背景干净，光线均匀，细节清晰。",
  ].join("\n");
}

export function CharacterGeneration() {
  const navigate = useNavigate();
  const { projectId } = useParams();
  const pid = projectId ?? "";

  const { data: characters = [], isLoading } = useCharacters(pid);
  const extract = useExtractCharacters(pid);
  const saveCharacter = useSaveCharacter(pid);
  const generateImage = useGenerateCharacterImage(pid);
  const uploadImage = useUploadCharacterImage(pid);
  const deleteChar = useDeleteCharacter(pid);
  const saveState = useSaveCharacterState(pid);
  const generateStateImage = useGenerateCharacterStateImage(pid);
  const deleteState = useDeleteCharacterState(pid);

  const [activeTab] = useState<SettingsTab>("characters");
  const [generatingImageId, setGeneratingImageId] = useState<string | null>(null);
  const [generatingStateId, setGeneratingStateId] = useState<string | null>(null);
  const [showModal, setShowModal] = useState(false);
  const [editingChar, setEditingChar] = useState<Partial<Character>>(emptyForm());
  const [imagePreviews, setImagePreviews] = useState<ImageState>(emptyImageState());
  const [pendingFiles, setPendingFiles] = useState<PendingFiles>(emptyPendingFiles());
  const [stateDraft, setStateDraft] = useState<CharacterStateDraft>(emptyStateDraft());
  const [saving, setSaving] = useState(false);
  const [savingState, setSavingState] = useState(false);
  const [previewImage, setPreviewImage] = useState<{ src: string; title: string } | null>(null);

  const editingCharacterId = editingChar.id ? String(editingChar.id) : "";
  const { data: characterStates = [] } = useCharacterStates(pid, editingCharacterId);

  const fileInputRefs = useRef<Record<CharacterViewType, HTMLInputElement | null>>({
    front: null,
    side: null,
    back: null,
  });

  const settingsTabs = [
    { id: "characters" as SettingsTab, label: "角色", icon: Users, path: `/project/${projectId}/characters` },
    { id: "scenes" as SettingsTab, label: "场景", icon: MapPin, path: `/project/${projectId}/scenes` },
    { id: "props" as SettingsTab, label: "道具", icon: Box, path: `/project/${projectId}/props` },
  ];

  const roleTypeLabel = ROLE_TYPE_LABELS;

  const generatedCount = characters.filter((character) => hasCharacterImage(character)).length;
  const generatingCount = characters.filter((character) => character.status === "generating").length;

  const statusBadge = (status: string) => {
    if (status === "ready") {
      return <span className="px-2 py-0.5 rounded text-xs bg-green-500/10 text-green-600">三视图已完成</span>;
    }
    if (status === "generating") {
      return (
        <span className="px-2 py-0.5 rounded text-xs bg-blue-500/10 text-blue-600 flex items-center gap-1">
          <Loader2 className="w-3 h-3 animate-spin" />
          生成中
        </span>
      );
    }
    if (status === "failed") {
      return <span className="px-2 py-0.5 rounded text-xs bg-red-500/10 text-red-600">生成失败</span>;
    }
    return <span className="px-2 py-0.5 rounded text-xs bg-yellow-500/10 text-yellow-700">待生成</span>;
  };

  const handleExtract = () => {
    extract.mutate(undefined, {
      onError: (error) => alert(`提取失败: ${(error as Error).message}`),
    });
  };

  const openCreate = () => {
    revokeImagePreviews(imagePreviews);
    setEditingChar(emptyForm());
    setImagePreviews(emptyImageState());
    setPendingFiles(emptyPendingFiles());
    setStateDraft(emptyStateDraft());
    fileInputRefs.current.front && (fileInputRefs.current.front.value = "");
    setShowModal(true);
  };

  const openEdit = (character: Character) => {
    revokeImagePreviews(imagePreviews);
    const prompt = character.imagePrompt?.trim() ?? "";
    setEditingChar({
      ...character,
      imagePrompt: !prompt || isMostlyEnglishPrompt(prompt)
        ? buildChineseCharacterPrompt(character)
        : prompt,
    });
    setImagePreviews({
      front: getCharacterViewUrl(character, "front"),
      side: getCharacterViewUrl(character, "side"),
      back: getCharacterViewUrl(character, "back"),
    });
    setPendingFiles(emptyPendingFiles());
    setStateDraft(emptyStateDraft());
    fileInputRefs.current.front && (fileInputRefs.current.front.value = "");
    setShowModal(true);
  };

  const closeModal = () => {
    revokeImagePreviews(imagePreviews);
    setShowModal(false);
    setPendingFiles(emptyPendingFiles());
    fileInputRefs.current.front && (fileInputRefs.current.front.value = "");
  };

  const handleFileSelect = (viewType: CharacterViewType, e: ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (!file) return;

    revokeObjectPreview(imagePreviews[viewType]);
    setPendingFiles((current) => ({ ...current, [viewType]: file }));
    setImagePreviews((current) => ({ ...current, [viewType]: URL.createObjectURL(file) }));
    e.target.value = "";
  };

  const clearImage = (viewType: CharacterViewType) => {
    revokeObjectPreview(imagePreviews[viewType]);
    setPendingFiles((current) => ({ ...current, [viewType]: null }));
    setImagePreviews((current) => ({
      ...current,
      [viewType]: editingChar.id ? getCharacterViewUrl(editingChar, viewType) : null,
    }));
    if (fileInputRefs.current[viewType]) {
      fileInputRefs.current[viewType]!.value = "";
    }
  };

  const handleSave = async () => {
    if (!editingChar.name?.trim()) return;

    setSaving(true);
    try {
      const saved = await saveCharacter.mutateAsync({ ...editingChar, status: editingChar.status ?? "draft" });
      const pendingFile = pendingFiles.front;
      if (pendingFile) {
        await uploadImage.mutateAsync({ id: String(saved.id), file: pendingFile, viewType: "front" });
      }
      closeModal();
    } catch (error) {
      alert(`保存失败: ${(error as Error).message}`);
    } finally {
      setSaving(false);
    }
  };

  const handleGenerateImage = async (character: Character) => {
    setGeneratingImageId(character.id);
    generateImage.mutate(character.id, {
      onSettled: () => setGeneratingImageId(null),
      onError: (error) => alert(`三视图生成失败: ${(error as Error).message}`),
    });
  };

  const handleGenerateFromModal = async () => {
    if (!editingChar.name?.trim()) return;

    const draft: Partial<Character> = {
      ...editingChar,
      status: editingChar.status ?? "draft",
      imagePrompt: editingChar.imagePrompt?.trim() || buildChineseCharacterPrompt(editingChar),
    };
    setSaving(true);
    setGeneratingImageId(editingChar.id ? String(editingChar.id) : "draft");
    try {
      const saved = await saveCharacter.mutateAsync(draft);
      const generated = await generateImage.mutateAsync(String(saved.id));
      revokeImagePreviews(imagePreviews);
      setEditingChar({
        ...generated,
        imagePrompt: generated.imagePrompt ?? draft.imagePrompt,
      });
      setImagePreviews({
        front: getCharacterViewUrl(generated, "front"),
        side: getCharacterViewUrl(generated, "side"),
        back: getCharacterViewUrl(generated, "back"),
      });
      setPendingFiles(emptyPendingFiles());
      fileInputRefs.current.front && (fileInputRefs.current.front.value = "");
    } catch (error) {
      alert(`三视图生成失败: ${(error as Error).message}`);
    } finally {
      setSaving(false);
      setGeneratingImageId(null);
    }
  };

  const handleSaveState = async () => {
    if (!editingCharacterId || !stateDraft.name.trim()) return;
    setSavingState(true);
    try {
      const saved = await saveState.mutateAsync({
        characterId: editingCharacterId,
        state: {
          ...stateDraft,
          name: stateDraft.name.trim(),
          description: stateDraft.description?.trim() || null,
          prompt: stateDraft.prompt?.trim() || null,
          status: "draft",
        },
      });
      setStateDraft(emptyStateDraft());
      setGeneratingStateId(saved.id);
      generateStateImage.mutate(
        { characterId: editingCharacterId, stateId: saved.id },
        {
          onSettled: () => setGeneratingStateId(null),
          onError: (error) => alert(`状态图生成失败: ${(error as Error).message}`),
        }
      );
    } catch (error) {
      alert(`保存状态失败: ${(error as Error).message}`);
    } finally {
      setSavingState(false);
    }
  };

  const handleGenerateStateImage = (state: CharacterState) => {
    if (!editingCharacterId) return;
    setGeneratingStateId(state.id);
    generateStateImage.mutate(
      { characterId: editingCharacterId, stateId: state.id },
      {
        onSettled: () => setGeneratingStateId(null),
        onError: (error) => alert(`状态图生成失败: ${(error as Error).message}`),
      }
    );
  };

  const handleDeleteState = (state: CharacterState) => {
    if (!editingCharacterId || !confirm(`确定要删除状态「${state.name}」吗？`)) return;
    deleteState.mutate(
      { characterId: editingCharacterId, stateId: state.id },
      { onError: (error) => alert(`删除状态失败: ${(error as Error).message}`) }
    );
  };

  const handleDelete = (character: Character) => {
    if (!confirm(`确定要删除角色「${character.name}」吗？`)) return;
    deleteChar.mutate(character.id, {
      onError: (error) => alert(`删除失败: ${(error as Error).message}`),
    });
  };

  return (
    <div className="h-full overflow-auto bg-background relative">
      <div className="p-6 pb-24">
        <div className="max-w-7xl mx-auto">
          <div className="mb-6">
            <div className="flex items-center gap-3 mb-2">
              <div className="w-8 h-8 rounded-lg bg-gradient-to-br from-[#5b5ff9] to-[#8b5ff9] flex items-center justify-center">
                <Sparkles className="w-4 h-4 text-white" />
              </div>
              <h1 className="text-2xl font-semibold text-foreground">项目设定</h1>
            </div>
            <p className="text-sm text-muted-foreground">管理角色、场景和道具设定。角色形象已升级为标准三视图。</p>
          </div>

          <div className="flex items-center justify-between mb-6">
            <div className="flex items-center gap-2">
              {settingsTabs.map((tab) => {
                const Icon = tab.icon;
                return (
                  <button
                    key={tab.id}
                    onClick={() => navigate(tab.path)}
                    className={`flex items-center gap-2 px-4 py-2 rounded-lg text-sm transition-all ${
                      activeTab === tab.id
                        ? "bg-accent text-accent-foreground font-medium"
                        : "text-muted-foreground hover:text-foreground hover:bg-muted"
                    }`}
                  >
                    <Icon className="w-4 h-4" />
                    {tab.label}
                  </button>
                );
              })}
            </div>

            <div className="flex items-center gap-2">
              <button
                onClick={openCreate}
                className="px-4 py-2 rounded-lg border border-border bg-card text-foreground hover:bg-muted transition-all flex items-center gap-2 text-sm"
              >
                <Plus className="w-4 h-4" />
                手动添加
              </button>
              <button
                onClick={handleExtract}
                disabled={extract.isPending}
                className="px-4 py-2 rounded-lg bg-gradient-to-r from-[#5b5ff9] to-[#8b5ff9] text-white hover:shadow-md transition-all flex items-center gap-2 disabled:opacity-60"
              >
                {extract.isPending ? <Loader2 className="w-4 h-4 animate-spin" /> : <Sparkles className="w-4 h-4" />}
                AI提取角色
              </button>
            </div>
          </div>

          <div className="flex items-center justify-between mb-6 rounded-lg border border-border bg-card p-4">
            <div className="flex items-center gap-6">
              <div className="flex items-center gap-2">
                <span className="text-sm text-muted-foreground">角色总数</span>
                <span className="text-lg font-semibold text-foreground">{characters.length}</span>
              </div>
              <div className="flex items-center gap-2">
                <span className="text-sm text-muted-foreground">三视图完整</span>
                <span className="text-lg font-semibold text-green-600">{generatedCount}</span>
              </div>
              <div className="flex items-center gap-2">
                <span className="text-sm text-muted-foreground">生成中</span>
                <span className="text-lg font-semibold text-blue-600">{generatingCount}</span>
              </div>
            </div>
          </div>

          {!isLoading && characters.length === 0 && (
            <div className="rounded-xl border border-dashed border-border bg-card p-12 text-center">
              <Users className="w-12 h-12 text-muted-foreground mx-auto mb-4" />
              <h3 className="text-lg font-medium text-foreground mb-2">暂无角色</h3>
              <p className="text-sm text-muted-foreground mb-6">
                角色设定页现在支持三视图展示。你可以手动添加角色，或从大纲和分集里自动提取。
              </p>
              <div className="flex items-center justify-center gap-3">
                <button
                  onClick={openCreate}
                  className="px-5 py-2.5 rounded-lg border border-border bg-card text-foreground hover:bg-muted transition-all flex items-center gap-2"
                >
                  <Plus className="w-4 h-4" />
                  手动添加
                </button>
                <button
                  onClick={handleExtract}
                  disabled={extract.isPending}
                  className="px-6 py-2.5 rounded-lg bg-gradient-to-r from-[#5b5ff9] to-[#8b5ff9] text-white hover:shadow-md transition-all flex items-center gap-2 disabled:opacity-60"
                >
                  {extract.isPending ? <Loader2 className="w-4 h-4 animate-spin" /> : <Sparkles className="w-4 h-4" />}
                  AI提取角色
                </button>
              </div>
            </div>
          )}

          {isLoading && (
            <div className="flex items-center justify-center py-16">
              <Loader2 className="w-8 h-8 animate-spin text-muted-foreground" />
            </div>
          )}

          {characters.length > 0 && (
            <div className="grid grid-cols-2 gap-6">
              {characters.map((character) => {
                const mainImage = getCharacterViewUrl(character, "front")
                  ?? getCharacterViewUrl(character, "side")
                  ?? getCharacterViewUrl(character, "back");
                const isGenerating = generatingImageId === character.id || character.status === "generating";

                return (
                  <div key={character.id} className="rounded-xl border border-border bg-card overflow-hidden">
                    <div className="p-5 border-b border-border">
                      <div className="flex items-start justify-between mb-4">
                        <div className="flex items-start gap-3">
                          <div className="w-14 h-14 rounded-full overflow-hidden bg-muted flex items-center justify-center">
                            {mainImage ? (
                              <button
                                type="button"
                                onClick={() => setPreviewImage({ src: mainImage, title: character.name })}
                                className="block h-full w-full cursor-zoom-in"
                              >
                                <img src={mainImage} alt={character.name} className="w-full h-full object-cover" />
                              </button>
                            ) : (
                              <ImageIcon className="w-6 h-6 text-muted-foreground" />
                            )}
                          </div>
                          <div>
                            <h3 className="text-lg font-semibold text-foreground mb-1">{character.name}</h3>
                            <div className="flex items-center gap-2 flex-wrap">
                              <span className="text-xs text-muted-foreground">{roleTypeLabel[character.roleType] ?? character.roleType}</span>
                              {statusBadge(character.status)}
                            </div>
                          </div>
                        </div>

                        <div className="flex items-center gap-1">
                          <button
                            onClick={() => openEdit(character)}
                            className="p-2 rounded-lg border border-border bg-card text-foreground hover:bg-muted transition-colors"
                          >
                            <Edit2 className="w-4 h-4" />
                          </button>
                          <button
                            onClick={() => handleDelete(character)}
                            className="p-2 rounded-lg border border-border bg-card text-destructive hover:bg-destructive/10 transition-colors"
                          >
                            <Trash2 className="w-4 h-4" />
                          </button>
                        </div>
                      </div>

                      <div className="space-y-1.5 text-sm">
                        <div className="flex">
                          <span className="text-muted-foreground w-16">性别</span>
                          <span className="text-foreground">{character.gender === "male" ? "男" : character.gender === "female" ? "女" : character.gender}</span>
                        </div>
                        <div className="flex">
                          <span className="text-muted-foreground w-16">年龄</span>
                          <span className="text-foreground">{character.age || "未填写"}</span>
                        </div>
                        <div className="flex">
                          <span className="text-muted-foreground w-16">性格</span>
                          <span className="text-foreground">{character.personality || "未填写"}</span>
                        </div>
                        <div className="flex">
                          <span className="text-muted-foreground w-16">服装</span>
                          <span className="text-foreground line-clamp-1">{character.costume || "未填写"}</span>
                        </div>
                        <div className="flex">
                          <span className="text-muted-foreground w-16">外貌</span>
                          <span className="text-foreground line-clamp-1">{character.visualHint || "未填写"}</span>
                        </div>
                      </div>
                    </div>

                    <div className="p-5 bg-muted/20">
                      <div className="flex items-center justify-between mb-3">
                        <div>
                          <span className="text-xs text-muted-foreground font-medium">角色三视图</span>
                          <p className="text-[11px] text-muted-foreground mt-0.5">大头像 + 正/侧/背全身拼图，用于角色一致性参考。</p>
                        </div>
                        <button
                          onClick={() => handleGenerateImage(character)}
                          disabled={isGenerating}
                          className="px-3 py-1.5 rounded-md border border-border bg-card text-foreground hover:bg-muted transition-colors text-xs flex items-center gap-1.5 disabled:opacity-50"
                        >
                          {isGenerating ? <Loader2 className="w-3 h-3 animate-spin" /> : <RefreshCw className="w-3 h-3" />}
                          {hasCharacterImage(character) ? "重新生成三视图" : "AI生成三视图"}
                        </button>
                      </div>

                      {(() => {
                        const sheetUrl = getCharacterSheetUrl(character);
                        if (sheetUrl) {
                          return (
                            <div className="rounded-lg overflow-hidden bg-white border border-border">
                              <button
                                type="button"
                                onClick={() => setPreviewImage({ src: sheetUrl, title: `${character.name} - 角色三视图` })}
                                className="block w-full cursor-zoom-in"
                              >
                                <img
                                  src={sheetUrl}
                                  alt={`${character.name}-角色三视图`}
                                  className="w-full h-auto object-contain"
                                />
                              </button>
                            </div>
                          );
                        }
                        const viewImages = CHARACTER_VIEWS
                          .map((view) => ({ ...view, imageUrl: getCharacterViewUrl(character, view.type) }))
                          .filter((view) => view.imageUrl);
                        if (viewImages.length === 0) {
                          return (
                            <div className="aspect-[16/9] rounded-lg overflow-hidden bg-white border border-border">
                              <div className="w-full h-full flex flex-col items-center justify-center gap-2">
                                {isGenerating ? (
                                  <>
                                    <Loader2 className="w-6 h-6 text-primary animate-spin" />
                                    <span className="text-xs text-muted-foreground">生成中</span>
                                  </>
                                ) : (
                                  <>
                                    <ImageIcon className="w-6 h-6 text-muted-foreground" />
                                    <span className="text-xs text-muted-foreground">待补充</span>
                                  </>
                                )}
                              </div>
                            </div>
                          );
                        }
                        return (
                          <div className={`grid gap-3 ${viewImages.length === 1 ? "grid-cols-1" : viewImages.length === 2 ? "grid-cols-2" : "grid-cols-3"}`}>
                            {viewImages.map((view) => {
                              const imageUrl = view.imageUrl!;
                              return (
                                <div key={view.type}>
                                  <div className="text-[11px] text-muted-foreground mb-1 text-center">{view.label}</div>
                                  <div className="aspect-[3/4] rounded-lg overflow-hidden bg-white border border-border">
                                    <button
                                      type="button"
                                      onClick={() => setPreviewImage({ src: imageUrl, title: `${character.name} - ${view.label}` })}
                                      className="block h-full w-full cursor-zoom-in"
                                    >
                                      <img src={imageUrl} alt={`${character.name}-${view.label}`} className="w-full h-full object-cover" />
                                    </button>
                                  </div>
                                </div>
                              );
                            })}
                          </div>
                        );
                      })()}

                    </div>
                  </div>
                );
              })}
            </div>
          )}
        </div>
      </div>

      <div className="fixed bottom-0 left-0 right-0 border-t border-border bg-card/95 backdrop-blur-sm px-6 py-3 z-10">
        <div className="max-w-7xl mx-auto flex items-center justify-end gap-3">
          <button
            onClick={() => navigate(`/project/${projectId}/scenes`)}
            className="px-6 py-2 rounded-lg bg-gradient-to-r from-[#5b5ff9] to-[#8b5ff9] text-white hover:shadow-md transition-all flex items-center gap-2 text-sm font-medium"
          >
            <span>继续生成场景设定</span>
            <ArrowRight className="w-5 h-5" />
          </button>
        </div>
      </div>

      {showModal && (
        <div className="fixed inset-0 bg-black/40 backdrop-blur-sm flex items-center justify-center z-50 p-6">
          <div className="bg-card rounded-2xl border border-border w-full max-w-3xl max-h-[90vh] flex flex-col">
            <div className="flex items-center justify-between p-6 border-b border-border flex-shrink-0">
              <div>
                <h2 className="text-lg font-semibold text-foreground">{editingChar.id ? "编辑角色" : "手动添加角色"}</h2>
                <p className="text-sm text-muted-foreground mt-1">角色图已合并为单张图片，只需要维护图片和生成提示词。</p>
              </div>
              <button onClick={closeModal} className="p-2 hover:bg-muted rounded-lg transition-colors">
                <X className="w-5 h-5 text-muted-foreground" />
              </button>
            </div>

            <div className="overflow-auto p-6 space-y-5 flex-1">
              <div className="rounded-xl border border-border bg-muted/20 p-4">
                <div className="flex items-center justify-between mb-3">
                  <div>
                    <label className="block text-sm font-medium text-foreground">角色图片（可选）</label>
                    <p className="text-xs text-muted-foreground mt-1">上传大头照与三视图合并后的单张角色参考图。</p>
                  </div>
                  {pendingFiles.front && (
                    <button onClick={() => clearImage("front")} className="text-xs text-destructive hover:underline">
                      撤销更换
                    </button>
                  )}
                </div>
                <div
                  className="aspect-[16/9] rounded-lg border-2 border-dashed border-border bg-background flex items-center justify-center cursor-pointer hover:border-primary transition-colors overflow-hidden"
                  onClick={() => fileInputRefs.current.front?.click()}
                >
                  {imagePreviews.front ? (
                    <img src={imagePreviews.front} alt="角色图片预览" className="w-full h-full object-contain" />
                  ) : (
                    <div className="text-center px-4">
                      <Upload className="w-6 h-6 text-muted-foreground mx-auto mb-2" />
                      <span className="text-sm text-muted-foreground">点击上传角色图片</span>
                    </div>
                  )}
                </div>
                {imagePreviews.front ? (
                  <div className="mt-2 flex items-center gap-2">
                    <button
                      type="button"
                      onClick={() => fileInputRefs.current.front?.click()}
                      className="inline-flex items-center gap-1.5 rounded-md border border-border bg-card px-2.5 py-1 text-xs text-foreground hover:bg-muted"
                    >
                      <Upload className="h-3.5 w-3.5" />
                      更换图片
                    </button>
                    <button
                      type="button"
                      onClick={handleGenerateFromModal}
                      disabled={!editingChar.name?.trim() || saving || uploadImage.isPending || generateImage.isPending}
                      className="inline-flex items-center gap-1.5 rounded-md border border-primary/30 bg-primary/5 px-2.5 py-1 text-xs text-primary hover:bg-primary/10 disabled:opacity-50"
                    >
                      {generatingImageId === (editingChar.id ? String(editingChar.id) : "draft") ? (
                        <Loader2 className="h-3.5 w-3.5 animate-spin" />
                      ) : (
                        <Sparkles className="h-3.5 w-3.5" />
                      )}
                      AI重新生成
                    </button>
                  </div>
                ) : (
                  <button
                    type="button"
                    onClick={handleGenerateFromModal}
                    disabled={!editingChar.name?.trim() || saving || uploadImage.isPending || generateImage.isPending}
                    className="mt-2 inline-flex items-center gap-1.5 rounded-md border border-primary/30 bg-primary/5 px-2.5 py-1 text-xs text-primary hover:bg-primary/10 disabled:opacity-50"
                  >
                    {generatingImageId === (editingChar.id ? String(editingChar.id) : "draft") ? (
                      <Loader2 className="h-3.5 w-3.5 animate-spin" />
                    ) : (
                      <Sparkles className="h-3.5 w-3.5" />
                    )}
                    {editingChar.id ? "AI生成三视图" : "保存并AI生成"}
                  </button>
                )}
                <input
                  ref={(node) => {
                    fileInputRefs.current.front = node;
                  }}
                  type="file"
                  accept="image/*"
                  className="hidden"
                  onChange={(e) => handleFileSelect("front", e)}
                />
              </div>

              <div>
                <div className="mb-1 flex items-center justify-between gap-3">
                  <label className="block text-sm font-medium text-foreground">角色图片提示词</label>
                  <button
                    type="button"
                    onClick={() =>
                      setEditingChar({
                        ...editingChar,
                        imagePrompt: buildChineseCharacterPrompt(editingChar),
                      })
                    }
                    className="inline-flex items-center gap-1.5 rounded-md border border-border bg-card px-2.5 py-1 text-xs text-foreground hover:bg-muted"
                  >
                    <Sparkles className="h-3.5 w-3.5" />
                    生成中文提示词
                  </button>
                </div>
                <textarea
                  value={editingChar.imagePrompt ?? ""}
                  onChange={(e) => setEditingChar({ ...editingChar, imagePrompt: e.target.value })}
                  placeholder="用于生成或重生成角色参考图的提示词，可描述外貌、服装、气质和画面要求。"
                  rows={6}
                  className="w-full px-3 py-2 rounded-lg border border-border bg-background text-foreground placeholder-muted-foreground resize-none focus:outline-none focus:ring-2 focus:ring-primary/20"
                />
              </div>

              <div>
                <label className="block text-sm font-medium text-foreground mb-1">
                  角色名称
                  <span className="text-destructive ml-1">*</span>
                </label>
                <input
                  type="text"
                  value={editingChar.name ?? ""}
                  onChange={(e) => setEditingChar({ ...editingChar, name: e.target.value })}
                  placeholder="例如：乔沫"
                  className="w-full px-3 py-2 rounded-lg border border-border bg-background text-foreground placeholder-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/20"
                />
              </div>

              <div className="grid grid-cols-2 gap-3">
                <div>
                  <label className="block text-sm font-medium text-foreground mb-1">角色类型</label>
                  <select
                    value={editingChar.roleType ?? "supporting"}
                    onChange={(e) => setEditingChar({ ...editingChar, roleType: e.target.value })}
                    className="w-full px-3 py-2 rounded-lg border border-border bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary/20 cursor-pointer"
                  >
                    {ROLE_TYPES.map((role) => (
                      <option key={role.value} value={role.value}>
                        {role.label}
                      </option>
                    ))}
                  </select>
                </div>
                <div>
                  <label className="block text-sm font-medium text-foreground mb-1">性别</label>
                  <select
                    value={editingChar.gender ?? "female"}
                    onChange={(e) => setEditingChar({ ...editingChar, gender: e.target.value })}
                    className="w-full px-3 py-2 rounded-lg border border-border bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary/20 cursor-pointer"
                  >
                    <option value="female">女</option>
                    <option value="male">男</option>
                  </select>
                </div>
              </div>

              <div>
                <label className="block text-sm font-medium text-foreground mb-1">年龄</label>
                <input
                  type="text"
                  value={editingChar.age ?? ""}
                  onChange={(e) => setEditingChar({ ...editingChar, age: e.target.value })}
                  placeholder="例如：28岁"
                  className="w-full px-3 py-2 rounded-lg border border-border bg-background text-foreground placeholder-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/20"
                />
              </div>

              <div>
                <label className="block text-sm font-medium text-foreground mb-1">性格描述</label>
                <textarea
                  value={editingChar.personality ?? ""}
                  onChange={(e) => setEditingChar({ ...editingChar, personality: e.target.value })}
                  placeholder="例如：善良坚韧，外柔内刚，遇事很有主见。"
                  rows={2}
                  className="w-full px-3 py-2 rounded-lg border border-border bg-background text-foreground placeholder-muted-foreground resize-none focus:outline-none focus:ring-2 focus:ring-primary/20"
                />
              </div>

              <div>
                <label className="block text-sm font-medium text-foreground mb-1">外貌特征</label>
                <textarea
                  value={editingChar.visualHint ?? ""}
                  onChange={(e) => setEditingChar({ ...editingChar, visualHint: e.target.value })}
                  placeholder="例如：黑长直，杏眼，气质清冷，身形修长。"
                  rows={2}
                  className="w-full px-3 py-2 rounded-lg border border-border bg-background text-foreground placeholder-muted-foreground resize-none focus:outline-none focus:ring-2 focus:ring-primary/20"
                />
              </div>

              <div>
                <label className="block text-sm font-medium text-foreground mb-1">服装描述</label>
                <textarea
                  value={editingChar.costume ?? ""}
                  onChange={(e) => setEditingChar({ ...editingChar, costume: e.target.value })}
                  placeholder="例如：浅色风衣搭配衬衫长裤，整体简洁利落。"
                  rows={2}
                  className="w-full px-3 py-2 rounded-lg border border-border bg-background text-foreground placeholder-muted-foreground resize-none focus:outline-none focus:ring-2 focus:ring-primary/20"
                />
              </div>

              <div className="rounded-xl border border-border bg-muted/20 p-4 space-y-4">
                <div className="flex items-center justify-between gap-3">
                  <div>
                    <h3 className="text-sm font-medium text-foreground">角色状态图</h3>
                    <p className="text-xs text-muted-foreground mt-1">基于角色基准图做图生图，适合换装、受伤、妆容、表情等状态。</p>
                  </div>
                  {!editingCharacterId && (
                    <span className="text-xs text-muted-foreground">保存角色后可添加</span>
                  )}
                </div>

                {editingCharacterId && (
                  <>
                    <div className="grid grid-cols-4 gap-3">
                      <div>
                        <label className="block text-xs text-foreground mb-1">状态名</label>
                        <input
                          type="text"
                          value={stateDraft.name}
                          onChange={(e) => setStateDraft({ ...stateDraft, name: e.target.value })}
                          placeholder="例如：黑裙雨夜"
                          className="w-full px-3 py-2 rounded-lg border border-border bg-background text-sm focus:outline-none focus:ring-2 focus:ring-primary/20"
                        />
                      </div>
                      <div>
                        <label className="block text-xs text-foreground mb-1">类型</label>
                        <select
                          value={stateDraft.stateType}
                          onChange={(e) => setStateDraft({ ...stateDraft, stateType: e.target.value })}
                          className="w-full px-3 py-2 rounded-lg border border-border bg-background text-sm focus:outline-none focus:ring-2 focus:ring-primary/20"
                        >
                          <option value="costume">服装</option>
                          <option value="accessory">饰品</option>
                          <option value="expression">表情</option>
                          <option value="injury">伤痕</option>
                          <option value="makeup">妆容</option>
                          <option value="skin">肤色</option>
                          <option value="hair">发型细节</option>
                          <option value="age">年龄状态</option>
                          <option value="other">其他</option>
                        </select>
                      </div>
                      <div className="col-span-2">
                        <label className="block text-xs text-foreground mb-1">状态描述</label>
                        <input
                          type="text"
                          value={stateDraft.description ?? ""}
                          onChange={(e) => setStateDraft({ ...stateDraft, description: e.target.value })}
                          placeholder="例如：同一张脸，换深色长裙，眼神疲惫"
                          className="w-full px-3 py-2 rounded-lg border border-border bg-background text-sm focus:outline-none focus:ring-2 focus:ring-primary/20"
                        />
                      </div>
                      <div className="col-span-4">
                        <label className="block text-xs text-foreground mb-1">图生图提示词</label>
                        <textarea
                          value={stateDraft.prompt ?? ""}
                          onChange={(e) => setStateDraft({ ...stateDraft, prompt: e.target.value })}
                          rows={2}
                          placeholder="只写需要变化的状态，不要重写成另一个角色。"
                          className="w-full px-3 py-2 rounded-lg border border-border bg-background text-sm resize-none focus:outline-none focus:ring-2 focus:ring-primary/20"
                        />
                      </div>
                    </div>

                    <div className="flex justify-end">
                      <button
                        onClick={handleSaveState}
                        disabled={!stateDraft.name.trim() || savingState}
                        className="px-4 py-2 rounded-lg bg-primary text-primary-foreground text-sm flex items-center gap-2 disabled:opacity-50"
                      >
                        {savingState ? <Loader2 className="w-4 h-4 animate-spin" /> : <Sparkles className="w-4 h-4" />}
                        保存并生成状态图
                      </button>
                    </div>

                    {characterStates.length > 0 && (
                      <div className="grid grid-cols-2 gap-3">
                        {characterStates.map((state) => {
                          const isStateGenerating = generatingStateId === state.id || state.status === "generating";
                          return (
                            <div key={state.id} className="rounded-lg border border-border bg-background overflow-hidden">
                              <div className="aspect-video bg-white flex items-center justify-center border-b border-border">
                                {state.imageUrl ? (
                                  <img src={state.imageUrl} alt={state.name} className="w-full h-full object-contain" />
                                ) : isStateGenerating ? (
                                  <Loader2 className="w-6 h-6 text-primary animate-spin" />
                                ) : (
                                  <ImageIcon className="w-6 h-6 text-muted-foreground" />
                                )}
                              </div>
                              <div className="p-3">
                                <div className="flex items-start justify-between gap-2">
                                  <div className="min-w-0">
                                    <p className="text-sm font-medium text-foreground truncate">{state.name}</p>
                                    <p className="text-xs text-muted-foreground truncate">{state.description || state.stateType}</p>
                                  </div>
                                  <button
                                    onClick={() => handleDeleteState(state)}
                                    className="p-1 rounded text-muted-foreground hover:text-destructive hover:bg-destructive/10"
                                  >
                                    <Trash2 className="w-3.5 h-3.5" />
                                  </button>
                                </div>
                                <button
                                  onClick={() => handleGenerateStateImage(state)}
                                  disabled={isStateGenerating}
                                  className="mt-3 w-full px-3 py-1.5 rounded-md border border-border text-xs text-foreground hover:bg-muted disabled:opacity-50 flex items-center justify-center gap-1.5"
                                >
                                  {isStateGenerating ? <Loader2 className="w-3 h-3 animate-spin" /> : <RefreshCw className="w-3 h-3" />}
                                  {state.imageUrl ? "重生成状态图" : "生成状态图"}
                                </button>
                              </div>
                            </div>
                          );
                        })}
                      </div>
                    )}
                  </>
                )}
              </div>
            </div>

            <div className="flex gap-3 p-6 border-t border-border flex-shrink-0">
              <button
                onClick={closeModal}
                className="flex-1 px-4 py-2.5 rounded-lg border border-border bg-card text-foreground hover:bg-muted transition-colors"
              >
                取消
              </button>
              <button
                onClick={handleSave}
                disabled={!editingChar.name?.trim() || saving || uploadImage.isPending}
                className="flex-1 px-4 py-2.5 rounded-lg bg-gradient-to-r from-[#5b5ff9] to-[#8b5ff9] text-white hover:shadow-md transition-all flex items-center justify-center gap-2 disabled:opacity-50"
              >
                {saving || uploadImage.isPending ? <Loader2 className="w-4 h-4 animate-spin" /> : <Check className="w-4 h-4" />}
                保存
              </button>
            </div>
          </div>
        </div>
      )}
      <ImagePreviewModal
        src={previewImage?.src ?? null}
        title={previewImage?.title}
        alt={previewImage?.title}
        onClose={() => setPreviewImage(null)}
      />
    </div>
  );
}
