import { type ReactNode, useEffect, useMemo, useRef, useState } from 'react';

import { normalizeCustomWorldLandmarks } from '../data/customWorldSceneGraph';
import {
  generateCustomWorldLandmark,
  generateCustomWorldPlayableNpc,
  generateCustomWorldStoryNpc,
} from '../services/aiService';
import {
  Character,
  CustomWorldLandmark,
  CustomWorldNpc,
  CustomWorldPlayableNpc,
  CustomWorldProfile,
} from '../types';
import {
  CustomWorldEntityCatalog,
  type ResultTab,
} from './CustomWorldEntityCatalog';
import CustomWorldEntityEditorModal, {
  type CustomWorldEditorTarget,
} from './CustomWorldEntityEditorModal';

interface CustomWorldResultViewProps {
  profile: CustomWorldProfile;
  previewCharacters: Character[];
  isGenerating: boolean;
  progress: number;
  progressLabel: string;
  error: string | null;
  onBack: () => void;
  onEditSetting?: () => void;
  onRegenerate?: () => void;
  onContinueExpand?: () => void;
  onEnterWorld?: () => void;
  onProfileChange: (profile: CustomWorldProfile) => void;
  readOnly?: boolean;
  backLabel?: string;
  editActionLabel?: string;
  regenerateActionLabel?: string;
  enterWorldActionLabel?: string;
  autoSaveState?: 'idle' | 'saving' | 'saved' | 'error';
  compactAgentResultMode?: boolean;
}

type EntityGenerationKind = 'playable' | 'story' | 'landmark';

type PendingGeneratedEntity = {
  id: string;
  kind: EntityGenerationKind;
  title: string;
  progress: number;
  phaseLabel: string;
};

type RecentGeneratedIds = Record<EntityGenerationKind, string[]>;

type CustomWorldAssetDebugEntry = {
  id: string;
  label: string;
  imageSrc: string;
  kind: 'playable' | 'story' | 'landmark' | 'scene-act';
};

type AssetDebugLoadStatus = 'loading' | 'loaded' | 'error';

const CUSTOM_WORLD_ASSET_DEBUG_QUERY_KEY = 'debugCustomWorldAssets';
const CUSTOM_WORLD_ASSET_DEBUG_STORAGE_KEY =
  'genarrative.debug.customWorldAssets';

function shouldEnableCustomWorldAssetDebugPanel() {
  if (!import.meta.env.DEV || typeof window === 'undefined') {
    return false;
  }

  const searchParams = new URLSearchParams(window.location.search);
  if (searchParams.get(CUSTOM_WORLD_ASSET_DEBUG_QUERY_KEY) === '1') {
    return true;
  }

  return (
    window.localStorage.getItem(CUSTOM_WORLD_ASSET_DEBUG_STORAGE_KEY) === '1'
  );
}

function collectCustomWorldAssetDebugEntries(
  profile: CustomWorldProfile,
): CustomWorldAssetDebugEntry[] {
  const playableEntries = profile.playableNpcs
    .map((role) => {
      const imageSrc = role.imageSrc?.trim() || '';
      if (!imageSrc) {
        return null;
      }

      return {
        id: `playable:${role.id}`,
        label: `${role.name}主形象`,
        imageSrc,
        kind: 'playable' as const,
      };
    })
    .filter(
      (entry): entry is CustomWorldAssetDebugEntry => Boolean(entry),
    );
  const storyEntries = profile.storyNpcs
    .map((role) => {
      const imageSrc = role.imageSrc?.trim() || '';
      if (!imageSrc) {
        return null;
      }

      return {
        id: `story:${role.id}`,
        label: `${role.name}场景角色主图`,
        imageSrc,
        kind: 'story' as const,
      };
    })
    .filter(
      (entry): entry is CustomWorldAssetDebugEntry => Boolean(entry),
    );
  const landmarkEntries = profile.landmarks
    .map((landmark) => {
      const imageSrc = landmark.imageSrc?.trim() || '';
      if (!imageSrc) {
        return null;
      }

      return {
        id: `landmark:${landmark.id}`,
        label: `${landmark.name}场景主图`,
        imageSrc,
        kind: 'landmark' as const,
      };
    })
    .filter(
      (entry): entry is CustomWorldAssetDebugEntry => Boolean(entry),
    );
  const sceneActEntries =
    profile.sceneChapterBlueprints?.flatMap((chapter) =>
      chapter.acts
        .map((act) => {
          const imageSrc = act.backgroundImageSrc?.trim() || '';
          if (!imageSrc) {
            return null;
          }

          return {
            id: `scene-act:${chapter.id}:${act.id}`,
            label: `${chapter.title || chapter.sceneId} / ${act.title}幕图`,
            imageSrc,
            kind: 'scene-act' as const,
          };
        })
        .filter(
          (entry): entry is CustomWorldAssetDebugEntry => Boolean(entry),
        ),
    ) ?? [];

  return [
    ...playableEntries,
    ...storyEntries,
    ...landmarkEntries,
    ...sceneActEntries,
  ];
}

function resolveAssetDebugStatusLabel(status: AssetDebugLoadStatus | undefined) {
  if (status === 'loaded') {
    return '已加载';
  }
  if (status === 'error') {
    return '加载失败';
  }
  return '检测中';
}

function resolveAssetDebugSummary(profile: CustomWorldProfile) {
  return [
    {
      label: '可扮演角色主图',
      value: `${profile.playableNpcs.filter((role) => Boolean(role.imageSrc?.trim())).length}/${profile.playableNpcs.length}`,
    },
    {
      label: '场景角色主图',
      value: `${profile.storyNpcs.filter((role) => Boolean(role.imageSrc?.trim())).length}/${profile.storyNpcs.length}`,
    },
    {
      label: '场景主图',
      value: `${profile.landmarks.filter((landmark) => Boolean(landmark.imageSrc?.trim())).length}/${profile.landmarks.length}`,
    },
    {
      label: '分幕图',
      value: `${profile.sceneChapterBlueprints?.reduce(
        (sum, chapter) =>
          sum +
          chapter.acts.filter((act) => Boolean(act.backgroundImageSrc?.trim()))
            .length,
        0,
      ) ?? 0}/${
        profile.sceneChapterBlueprints?.reduce(
          (sum, chapter) => sum + chapter.acts.length,
          0,
        ) ?? 0
      }`,
    },
  ];
}

function SmallButton({
  onClick,
  children,
  tone = 'default',
  disabled = false,
}: {
  onClick: () => void;
  children: ReactNode;
  tone?: 'default' | 'sky';
  disabled?: boolean;
}) {
  return (
    <button
      type="button"
      onClick={onClick}
      disabled={disabled}
      className={`${
        tone === 'sky'
          ? 'platform-button platform-button--primary'
          : 'platform-button platform-button--ghost'
      } min-h-0 rounded-full px-3 py-2 text-sm ${disabled ? 'cursor-not-allowed opacity-45' : ''}`}
    >
      {children}
    </button>
  );
}

function getCreateTargetByTab(
  activeTab: ResultTab,
): CustomWorldEditorTarget | null {
  if (activeTab === 'playable') return { kind: 'playable', mode: 'create' };
  if (activeTab === 'story') return { kind: 'story', mode: 'create' };
  if (activeTab === 'landmarks') return { kind: 'landmark', mode: 'create' };
  return null;
}

function getCreateLabelByTab(activeTab: ResultTab) {
  if (activeTab === 'playable') return '新增可扮演角色';
  if (activeTab === 'story') return '新增场景角色';
  if (activeTab === 'landmarks') return '新增场景';
  return '';
}

function createPendingGeneratedEntity(
  kind: EntityGenerationKind,
): PendingGeneratedEntity {
  return {
    id: `pending-${kind}-${Date.now()}`,
    kind,
    title:
      kind === 'playable'
        ? '新可扮演角色'
        : kind === 'story'
          ? '新场景角色'
          : '新场景',
    progress: 8,
    phaseLabel: '正在整理世界上下文',
  };
}

function resolvePendingPhaseLabel(
  kind: EntityGenerationKind,
  progress: number,
) {
  if (progress < 28) {
    return '正在整理世界上下文';
  }
  if (progress < 72) {
    return kind === 'landmark' ? '正在推理场景结构' : '正在推理角色结构';
  }
  return '正在回写结果';
}

function prependPlayableNpc(
  profile: CustomWorldProfile,
  npc: CustomWorldPlayableNpc,
) {
  return {
    ...profile,
    playableNpcs: [npc, ...profile.playableNpcs],
  } satisfies CustomWorldProfile;
}

function prependStoryNpc(profile: CustomWorldProfile, npc: CustomWorldNpc) {
  return {
    ...profile,
    storyNpcs: [npc, ...profile.storyNpcs],
  } satisfies CustomWorldProfile;
}

function prependLandmark(
  profile: CustomWorldProfile,
  landmark: CustomWorldLandmark,
) {
  return {
    ...profile,
    landmarks: normalizeCustomWorldLandmarks({
      landmarks: [landmark, ...profile.landmarks],
      storyNpcs: profile.storyNpcs,
    }),
  } satisfies CustomWorldProfile;
}

function removeStoryNpcsFromProfile(
  profile: CustomWorldProfile,
  ids: string[],
) {
  const idSet = new Set(ids);
  const nextStoryNpcs = profile.storyNpcs.filter((npc) => !idSet.has(npc.id));

  return {
    ...profile,
    storyNpcs: nextStoryNpcs,
    landmarks: normalizeCustomWorldLandmarks({
      landmarks: profile.landmarks.map((landmark) => ({
        ...landmark,
        sceneNpcIds: landmark.sceneNpcIds.filter((npcId) => !idSet.has(npcId)),
      })),
      storyNpcs: nextStoryNpcs,
    }),
  } satisfies CustomWorldProfile;
}

function removeLandmarksFromProfile(
  profile: CustomWorldProfile,
  ids: string[],
) {
  const idSet = new Set(ids);
  const nextLandmarks = profile.landmarks.filter(
    (landmark) => !idSet.has(landmark.id),
  );

  return {
    ...profile,
    landmarks: normalizeCustomWorldLandmarks({
      landmarks: nextLandmarks.map((landmark) => ({
        ...landmark,
        connections: landmark.connections.filter(
          (connection) => !idSet.has(connection.targetLandmarkId),
        ),
      })),
      storyNpcs: profile.storyNpcs,
    }),
  } satisfies CustomWorldProfile;
}

export function CustomWorldResultView({
  profile,
  previewCharacters,
  isGenerating,
  progress,
  progressLabel,
  error,
  onBack,
  onEditSetting,
  onRegenerate: triggerRegenerate,
  onContinueExpand,
  onEnterWorld,
  onProfileChange,
  readOnly = false,
  backLabel = '返回',
  editActionLabel = '修改设定',
  regenerateActionLabel = '重新生成',
  enterWorldActionLabel = '进入世界',
  autoSaveState = 'idle',
  compactAgentResultMode = false,
}: CustomWorldResultViewProps) {
  const [editorTarget, setEditorTarget] =
    useState<CustomWorldEditorTarget | null>(null);
  const [activeTab, setActiveTab] = useState<ResultTab>('world');
  const [pendingGeneratedEntity, setPendingGeneratedEntity] =
    useState<PendingGeneratedEntity | null>(null);
  const [recentGeneratedIds, setRecentGeneratedIds] = useState<RecentGeneratedIds>(
    {
      playable: [],
      story: [],
      landmark: [],
    },
  );
  const [localGenerationError, setLocalGenerationError] = useState<string | null>(
    null,
  );
  const pendingProgressTimerRef = useRef<number | null>(null);
  const assetDebugEnabled = useMemo(
    () => shouldEnableCustomWorldAssetDebugPanel(),
    [],
  );
  const assetDebugEntries = useMemo(
    () =>
      assetDebugEnabled ? collectCustomWorldAssetDebugEntries(profile) : [],
    [assetDebugEnabled, profile],
  );
  const assetDebugSummary = useMemo(
    () => (assetDebugEnabled ? resolveAssetDebugSummary(profile) : []),
    [assetDebugEnabled, profile],
  );
  const [assetDebugStatusMap, setAssetDebugStatusMap] = useState<
    Record<string, AssetDebugLoadStatus>
  >({});

  const createTarget = useMemo(
    () => getCreateTargetByTab(activeTab),
    [activeTab],
  );
  const createLabel = useMemo(
    () => getCreateLabelByTab(activeTab),
    [activeTab],
  );
  const stopPendingProgressTimer = () => {
    if (pendingProgressTimerRef.current !== null) {
      window.clearInterval(pendingProgressTimerRef.current);
      pendingProgressTimerRef.current = null;
    }
  };

  useEffect(() => () => stopPendingProgressTimer(), []);

  useEffect(() => {
    if (!assetDebugEnabled) {
      setAssetDebugStatusMap({});
      return;
    }

    if (assetDebugEntries.length === 0) {
      setAssetDebugStatusMap({});
      return;
    }

    let cancelled = false;
    const cleanupList: Array<() => void> = [];

    setAssetDebugStatusMap(
      Object.fromEntries(
        assetDebugEntries.map((entry) => [entry.id, 'loading' as const]),
      ),
    );

    assetDebugEntries.forEach((entry) => {
      const image = new Image();
      const updateStatus = (status: AssetDebugLoadStatus) => {
        if (cancelled) {
          return;
        }

        setAssetDebugStatusMap((current) => {
          if (current[entry.id] === status) {
            return current;
          }
          return {
            ...current,
            [entry.id]: status,
          };
        });
      };

      image.onload = () => updateStatus('loaded');
      image.onerror = () => updateStatus('error');
      image.src = entry.imageSrc;
      cleanupList.push(() => {
        image.onload = null;
        image.onerror = null;
      });
    });

    return () => {
      cancelled = true;
      cleanupList.forEach((cleanup) => cleanup());
    };
  }, [assetDebugEnabled, assetDebugEntries]);

  const startPendingProgress = (kind: EntityGenerationKind) => {
    stopPendingProgressTimer();
    setPendingGeneratedEntity(createPendingGeneratedEntity(kind));
    pendingProgressTimerRef.current = window.setInterval(() => {
      setPendingGeneratedEntity((current) => {
        if (!current || current.kind !== kind) {
          return current;
        }

        const nextProgress = Math.min(
          current.progress + (current.progress < 56 ? 11 : 5),
          88,
        );

        return {
          ...current,
          progress: nextProgress,
          phaseLabel: resolvePendingPhaseLabel(kind, nextProgress),
        };
      });
    }, 520);
  };

  const finishPendingProgress = () => {
    stopPendingProgressTimer();
    setPendingGeneratedEntity(null);
  };

  const markGeneratedAsRecent = (
    kind: EntityGenerationKind,
    generatedId: string,
  ) => {
    setRecentGeneratedIds((current) => ({
      ...current,
      [kind]: [generatedId, ...current[kind].filter((id) => id !== generatedId)].slice(
        0,
        6,
      ),
    }));
  };

  const handleGenerateEntity = async (kind: EntityGenerationKind) => {
    if (readOnly || isGenerating || pendingGeneratedEntity) {
      return;
    }

    setLocalGenerationError(null);
    startPendingProgress(kind);

    try {
      if (kind === 'playable') {
        const nextNpc = await generateCustomWorldPlayableNpc({ profile });
        onProfileChange(prependPlayableNpc(profile, nextNpc));
        markGeneratedAsRecent('playable', nextNpc.id);
      } else if (kind === 'story') {
        const nextNpc = await generateCustomWorldStoryNpc({ profile });
        onProfileChange(prependStoryNpc(profile, nextNpc));
        markGeneratedAsRecent('story', nextNpc.id);
      } else {
        const nextLandmark = await generateCustomWorldLandmark({ profile });
        onProfileChange(prependLandmark(profile, nextLandmark));
        markGeneratedAsRecent('landmark', nextLandmark.id);
      }
    } catch (generationError) {
      setLocalGenerationError(
        generationError instanceof Error
          ? generationError.message
          : '生成失败，请稍后重试。',
      );
    } finally {
      finishPendingProgress();
    }
  };

  const onRegenerate = () => {
    if (isGenerating || !triggerRegenerate) return;

    const confirmed = window.confirm(
      `确认重新生成“${profile.name}”吗？\n\n重新生成会重新生成当前世界中的所有信息，包括你修改和新增的所有内容。`,
    );
    if (!confirmed) return;

    triggerRegenerate();
  };

  const handleDeleteStoryNpcs = (ids: string[]) => {
    if (ids.length === 0) return;
    onProfileChange(removeStoryNpcsFromProfile(profile, ids));
  };

  const handleDeleteLandmarks = (ids: string[]) => {
    if (ids.length === 0) return;
    onProfileChange(removeLandmarksFromProfile(profile, ids));
  };
  const autoSaveBadge =
    autoSaveState === 'saved' ? (
      <div className="platform-pill platform-pill--success px-3 py-1 text-[11px]">
        已自动保存
      </div>
    ) : autoSaveState === 'saving' ? (
      <div className="platform-pill platform-pill--warm px-3 py-1 text-[11px]">
        保存中
      </div>
    ) : autoSaveState === 'error' ? (
      <div className="platform-pill platform-pill--rose px-3 py-1 text-[11px]">
        保存失败
      </div>
    ) : null;

  return (
    <div className="flex h-full min-h-0 flex-col">
      <div className="mb-4 flex items-center justify-between gap-3">
        <button
          type="button"
          onClick={onBack}
          disabled={isGenerating}
          className={`platform-button platform-button--ghost min-h-0 self-start px-3 py-1.5 text-[11px] ${isGenerating ? 'opacity-45' : ''}`}
        >
          {backLabel}
        </button>
        {autoSaveBadge}
      </div>

      <div className="min-h-0 flex-1 overflow-hidden">
        <CustomWorldEntityCatalog
          profile={profile}
          previewCharacters={previewCharacters}
          activeTab={activeTab}
          onActiveTabChange={setActiveTab}
          onEditTarget={setEditorTarget}
          onProfileChange={onProfileChange}
          onDeleteStoryNpcs={handleDeleteStoryNpcs}
          onDeleteLandmarks={handleDeleteLandmarks}
          createActionLabel={
            readOnly || compactAgentResultMode ? undefined : createLabel
          }
          onCreateAction={
            readOnly || compactAgentResultMode || !createTarget
              ? undefined
              : () => {
                  if (activeTab === 'playable') {
                    void handleGenerateEntity('playable');
                    return;
                  }
                  if (activeTab === 'story') {
                    void handleGenerateEntity('story');
                    return;
                  }
                  if (activeTab === 'landmarks') {
                    void handleGenerateEntity('landmark');
                    return;
                  }
                  setEditorTarget(createTarget);
                }
          }
          createActionDisabled={Boolean(
            isGenerating || pendingGeneratedEntity,
          )}
          pendingGeneratedEntity={pendingGeneratedEntity}
          recentGeneratedIds={recentGeneratedIds}
          readOnly={readOnly}
        />
      </div>

      {isGenerating && (
        <div className="platform-banner platform-banner--info mt-3 rounded-2xl px-4 py-4">
          <div className="flex items-center justify-between gap-3">
            <div className="text-sm font-semibold text-[var(--platform-text-strong)]">
              {progressLabel}
            </div>
            <div className="text-xs text-[var(--platform-text-base)]">
              {Math.round(progress)}%
            </div>
          </div>
          <div className="platform-progress-track mt-3 h-3 overflow-hidden rounded-full">
            <div
              className="h-full bg-[linear-gradient(90deg,#ff4f8b_0%,#ff8a73_48%,#ffd2a6_100%)] transition-[width] duration-300"
              style={{ width: `${Math.max(0, Math.min(100, progress))}%` }}
            />
          </div>
        </div>
      )}

      {error ? (
        <div className="platform-banner platform-banner--danger mt-3 rounded-2xl text-sm leading-6">
          {error}
        </div>
      ) : null}
      {!error && localGenerationError ? (
        <div className="platform-banner platform-banner--danger mt-3 rounded-2xl text-sm leading-6">
          {localGenerationError}
        </div>
      ) : null}
      {assetDebugEnabled ? (
        <div className="platform-surface platform-surface--soft mt-3 px-3.5 py-3">
          <div className="flex items-start justify-between gap-3">
            <div>
              <div className="text-xs font-bold tracking-[0.16em] text-white">
                资产诊断
              </div>
              <div className="mt-1 text-xs leading-6 text-zinc-500">
                仅开发模式显示，用来核对结果页当前拿到的图片字段和实际加载状态。
              </div>
            </div>
            <div className="platform-pill platform-pill--neutral px-2.5 py-1 text-[10px]">
              {assetDebugEntries.length}项
            </div>
          </div>
          <div className="mt-3 grid grid-cols-2 gap-2 xl:grid-cols-4">
            {assetDebugSummary.map((entry) => (
              <div
                key={entry.label}
                className="platform-subpanel rounded-2xl px-3 py-2"
              >
                <div className="text-[11px] text-zinc-500">{entry.label}</div>
                <div className="mt-1 text-sm font-semibold text-white">
                  {entry.value}
                </div>
              </div>
            ))}
          </div>
          <div className="mt-3 space-y-2">
            {assetDebugEntries.length > 0 ? (
              assetDebugEntries.map((entry) => (
                <div
                  key={entry.id}
                  className="platform-subpanel rounded-2xl px-3 py-2"
                >
                  <div className="flex items-start justify-between gap-3">
                    <div className="min-w-0">
                      <div className="text-sm font-semibold text-white">
                        {entry.label}
                      </div>
                      <div className="mt-1 break-all text-[11px] leading-5 text-zinc-400">
                        {entry.imageSrc}
                      </div>
                    </div>
                    <div className="platform-pill platform-pill--neutral px-2.5 py-1 text-[10px]">
                      {resolveAssetDebugStatusLabel(
                        assetDebugStatusMap[entry.id],
                      )}
                    </div>
                  </div>
                  <div className="mt-2">
                    <a
                      href={entry.imageSrc}
                      target="_blank"
                      rel="noreferrer"
                      aria-label={`打开 ${entry.label}`}
                      className="text-xs font-semibold text-amber-200 underline decoration-white/20 underline-offset-2"
                    >
                      打开原图
                    </a>
                  </div>
                </div>
              ))
            ) : (
              <div className="platform-subpanel rounded-2xl px-3 py-3 text-sm text-zinc-400">
                当前结果页 profile 里没有拿到任何可诊断的图片地址。
              </div>
            )}
          </div>
        </div>
      ) : null}

      <div className="mt-4 flex flex-col gap-3">
        {profile.generationStatus === 'key_only' ? (
          <div className="platform-banner platform-banner--warning rounded-2xl text-sm leading-6">
            当前世界处于快速预览模式，只生成了关键对象。继续补全后，系统会生成长尾场景角色与完整场景网络。
          </div>
        ) : null}
        <div className="flex items-center justify-end gap-3">
          {onEditSetting ? (
            <SmallButton onClick={onEditSetting}>{editActionLabel}</SmallButton>
          ) : null}
          {triggerRegenerate ? (
            <SmallButton onClick={onRegenerate} tone="sky">
              {regenerateActionLabel}
            </SmallButton>
          ) : null}
          {profile.generationStatus === 'key_only' && onContinueExpand ? (
            <SmallButton
              onClick={onContinueExpand}
              tone="sky"
              disabled={isGenerating}
            >
              继续补全世界
            </SmallButton>
          ) : null}
          {onEnterWorld ? (
            <button
              type="button"
              onClick={onEnterWorld}
              disabled={isGenerating}
              className={`platform-button platform-button--primary ${isGenerating ? 'opacity-55' : ''}`}
            >
              {enterWorldActionLabel}
            </button>
          ) : null}
        </div>
      </div>

      <CustomWorldEntityEditorModal
        profile={profile}
        target={editorTarget}
        onClose={() => setEditorTarget(null)}
        onProfileChange={onProfileChange}
      />
    </div>
  );
}
