import { AnimatePresence, motion } from 'motion/react';
import { type CSSProperties, useEffect, useMemo, useState } from 'react';

import { getCustomWorldSceneRelativePositionLabel } from '../data/customWorldSceneGraph';
import { getConnectedScenePresets } from '../data/scenePresets';
import { useResolvedAssetReadUrl } from '../hooks/useResolvedAssetReadUrl';
import { ScenePresetInfo, WorldType } from '../types';
import { CHROME_ICONS, getNineSliceStyle, UI_CHROME } from '../uiAssets';
import { PlatformActionButton } from './common/PlatformActionButton';
import { PlatformPillBadge } from './common/PlatformPillBadge';
import { PlatformSubpanel } from './common/PlatformSubpanel';
import { PixelCloseButton } from './PixelCloseButton';
import { PixelIcon } from './PixelIcon';

function buildSceneBackdropStyle(imageSrc?: string | null): CSSProperties {
  return {
    backgroundImage: imageSrc
      ? `linear-gradient(rgba(7,10,18,0.82), rgba(7,10,18,0.76)), url("${imageSrc}")`
      : 'linear-gradient(rgba(7,10,18,0.82), rgba(7,10,18,0.76))',
    backgroundSize: 'cover',
    backgroundPosition: 'center',
    backgroundRepeat: 'no-repeat',
  };
}

const MAP_NODE_MIN_HEIGHT_PX = 52;
const MAP_NODE_GAP_PX = 12;

function getMapDestinationStackHeight(count: number) {
  if (count <= 0) return MAP_NODE_MIN_HEIGHT_PX;
  return count * MAP_NODE_MIN_HEIGHT_PX + (count - 1) * MAP_NODE_GAP_PX;
}

function getMapDestinationCenterPercent(index: number, count: number) {
  const totalHeight = getMapDestinationStackHeight(count);
  const centerY = index * (MAP_NODE_MIN_HEIGHT_PX + MAP_NODE_GAP_PX) + MAP_NODE_MIN_HEIGHT_PX / 2;
  return (centerY / totalHeight) * 100;
}

function MudMapRoom({
  scene,
  label,
  compact = false,
  isInteractive = false,
  isSelected = false,
  description,
  onClick,
}: {
  key?: string;
  scene: ScenePresetInfo | null | undefined;
  label: string;
  compact?: boolean;
  isInteractive?: boolean;
  isSelected?: boolean;
  description?: string;
  onClick?: (() => void) | null;
}) {
  if (!scene) {
    return (
      <div
        className="pixel-nine-slice map-room-cell h-full min-h-[3.25rem] opacity-40"
        style={getNineSliceStyle(UI_CHROME.mapRoomCell)}
      />
    );
  }

  const content = (
    <div
      className={`pixel-nine-slice map-room-cell h-full font-mono ${compact ? 'text-[10px]' : 'text-[11px]'} leading-relaxed ${isInteractive ? 'transition-transform duration-150 hover:-translate-y-0.5 hover:brightness-110' : ''} ${isSelected ? 'brightness-125' : ''}`}
      style={getNineSliceStyle(UI_CHROME.mapRoomCell)}
    >
      <div className="flex min-h-[3.25rem] flex-col items-center justify-center px-3 py-2 text-center">
        <PlatformPillBadge
          tone="darkEmerald"
          size="xxs"
          className="tracking-[0.16em] text-emerald-100/85"
        >
          {label}
        </PlatformPillBadge>
        <div className={`mt-1 ${compact ? 'text-[13px]' : 'text-sm'} font-semibold leading-tight text-white`}>
          {scene.name}
        </div>
        {!compact && description ? (
          <div className="mt-2 text-[10px] leading-5 text-zinc-300/85">
            {description}
          </div>
        ) : null}
      </div>
    </div>
  );

  if (!isInteractive || !onClick) {
    return content;
  }

  return (
    <button type="button" onClick={onClick} className="block h-full w-full text-left">
      {content}
    </button>
  );
}

interface MapModalProps {
  isOpen: boolean;
  currentScenePreset: ScenePresetInfo | null;
  worldType: WorldType | null;
  onClose: () => void;
  onTravelToScene: (scene: ScenePresetInfo) => void;
  isTraveling?: boolean;
  canTravel?: boolean;
}

type MapConnectionEntry = {
  scene: ScenePresetInfo;
  label: string;
  summary: string;
};

function isMapConnectionEntry(
  entry: MapConnectionEntry | null,
): entry is MapConnectionEntry {
  return entry !== null;
}

function buildFallbackConnectionEntries(
  currentScenePreset: ScenePresetInfo | null,
  connectedScenes: ScenePresetInfo[],
) {
  const forwardSceneId = currentScenePreset?.forwardSceneId;
  const forwardScene =
    connectedScenes.find((scene) => scene.id === forwardSceneId) ?? null;
  const branchScenes = connectedScenes.filter((scene) => scene.id !== forwardSceneId);

  const fallbackEntries: Array<MapConnectionEntry | null> = [
    forwardScene
      ? ({
          scene: forwardScene,
          label: '前方',
          summary: '沿主路继续深入',
        } satisfies MapConnectionEntry)
      : null,
    ...branchScenes.map(
      (scene, index) =>
        ({
          scene,
          label: index === 0 ? '支路左侧' : index === 1 ? '支路右侧' : `支路${index + 1}`,
          summary: '可转向另一片区域',
        }) satisfies MapConnectionEntry,
    ),
  ];

  return fallbackEntries.filter(isMapConnectionEntry);
}

export function MapModal({
  isOpen,
  currentScenePreset,
  worldType,
  onClose,
  onTravelToScene,
  isTraveling = false,
  canTravel = true,
}: MapModalProps) {
  const [pendingScene, setPendingScene] = useState<MapConnectionEntry | null>(null);
  const {
    resolvedUrl: resolvedBackdropImageSrc,
    shouldResolve: shouldResolveBackdropImage,
  } = useResolvedAssetReadUrl(currentScenePreset?.imageSrc);

  const connectedScenes = useMemo(
    () =>
      worldType && currentScenePreset
        ? getConnectedScenePresets(worldType, currentScenePreset.id)
        : [],
    [currentScenePreset, worldType],
  );
  const connectionEntries = useMemo(() => {
    if (currentScenePreset?.connections?.length) {
      const entries: Array<MapConnectionEntry | null> = currentScenePreset.connections
        .map((connection) => {
          const scene = connectedScenes.find(
            (item) => item.id === connection.sceneId,
          );
          if (!scene) {
            return null;
          }

          return {
            scene,
            label: getCustomWorldSceneRelativePositionLabel(
              connection.relativePosition,
            ),
            summary: connection.summary,
          } satisfies MapConnectionEntry;
        });

      return entries.filter(isMapConnectionEntry);
    }

    return buildFallbackConnectionEntries(currentScenePreset, connectedScenes);
  }, [connectedScenes, currentScenePreset]);
  const sceneBackdropStyle = buildSceneBackdropStyle(
    resolvedBackdropImageSrc
      || (!shouldResolveBackdropImage ? currentScenePreset?.imageSrc : ''),
  );
  const destinationStackHeightPx = getMapDestinationStackHeight(connectionEntries.length);

  useEffect(() => {
    if (!isOpen) {
      setPendingScene(null);
    }
  }, [isOpen]);

  useEffect(() => {
    if (!pendingScene) return;
    if (!connectionEntries.some((scene) => scene.scene.id === pendingScene.scene.id)) {
      setPendingScene(null);
    }
  }, [connectionEntries, pendingScene]);

  const handleSceneSelect = (scene: MapConnectionEntry | null) => {
    if (!scene || scene.scene.id === currentScenePreset?.id) return;
    setPendingScene(scene);
  };

  const confirmTravel = () => {
    if (!pendingScene) return;
    onTravelToScene(pendingScene.scene);
    setPendingScene(null);
  };

  return (
    <AnimatePresence>
      {isOpen && currentScenePreset && (
        <motion.div
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          exit={{ opacity: 0 }}
          className="map-modal-overlay fixed inset-0 z-50 flex items-center justify-center bg-black/70 p-3 sm:p-4 backdrop-blur-sm"
          onClick={onClose}
        >
          <motion.div
            initial={{ opacity: 0, scale: 0.96, y: 8 }}
            animate={{ opacity: 1, scale: 1, y: 0 }}
            exit={{ opacity: 0, scale: 0.96, y: 8 }}
            transition={{ duration: 0.18, ease: 'easeOut' }}
            className="platform-remap-surface map-modal-shell pixel-nine-slice pixel-modal-shell relative flex max-h-[min(92vh,62rem)] w-full max-w-[min(96vw,64rem)] flex-col overflow-hidden shadow-[0_24px_80px_rgba(0,0,0,0.55)]"
            style={getNineSliceStyle(UI_CHROME.modalPanel)}
            onClick={event => event.stopPropagation()}
          >
            <div
              className="map-modal-backdrop pointer-events-none absolute inset-0"
              style={sceneBackdropStyle}
            />
            <div className="map-modal-shade pointer-events-none absolute inset-0 bg-[linear-gradient(180deg,rgba(2,6,23,0.18),rgba(2,6,23,0.68))]" />
            <div className="relative border-b border-white/10 px-4 py-3 sm:px-5 sm:py-4">
              <div className="min-w-0 pr-10">
                <div className="inline-flex items-center gap-2 text-[10px] tracking-[0.22em] text-emerald-300/75">
                  <PixelIcon src={CHROME_ICONS.map} className="h-3.5 w-3.5" />
                  <span>地图</span>
                </div>
              </div>
              <PixelCloseButton onClick={onClose} label="关闭地图" />
            </div>

            <div className="relative grid min-h-0 flex-1 gap-4 overflow-y-auto p-4 sm:p-5 md:grid-cols-[minmax(15rem,18rem)_minmax(0,1fr)] md:overflow-hidden">
              <div
                className="map-info-panel pixel-nine-slice pixel-panel min-w-0 font-mono text-[11px] leading-relaxed text-emerald-100/85 md:max-h-full md:self-start md:overflow-y-auto"
                style={getNineSliceStyle(UI_CHROME.infoPanel)}
              >
                <div className="text-emerald-200/75">当前位置</div>
                <div className="mt-1 text-sm text-white">{currentScenePreset.name}</div>
                <div className="mt-2 text-zinc-300">{currentScenePreset.description}</div>

                <div className="mt-2 space-y-1.5 text-zinc-300">
                  {connectionEntries.map((entry) => (
                    <div key={entry.scene.id}>{`- ${entry.label}：${entry.scene.name}`}</div>
                  ))}
                  {connectionEntries.length === 0 && <div>- 暂无</div>}
                </div>
              </div>

              <div className="min-h-0 p-1 font-mono md:overflow-y-auto">
                <div className="md:hidden">
                  <div className="grid grid-cols-[minmax(0,0.9fr)_2rem_minmax(0,1.1fr)] items-start gap-3">
                    <div className="w-full max-w-[7.5rem] justify-self-start" style={{ minHeight: `${destinationStackHeightPx}px` }}>
                      <MudMapRoom scene={currentScenePreset} label="当前位置" compact />
                    </div>
                    <div className="relative" style={{ minHeight: `${destinationStackHeightPx}px` }}>
                      {connectionEntries.length > 0 && (
                        <svg className="absolute inset-0 h-full w-full" preserveAspectRatio="none">
                          {connectionEntries.map((entry, index) => (
                            <line
                              key={`connector-${entry.scene.id}`}
                              x1="0"
                              y1={`${getMapDestinationCenterPercent(0, connectionEntries.length)}%`}
                              x2="100%"
                              y2={`${getMapDestinationCenterPercent(index, connectionEntries.length)}%`}
                              stroke="rgba(74, 222, 128, 0.35)"
                              strokeWidth="1.5"
                            />
                          ))}
                        </svg>
                      )}
                    </div>
                    <div className="space-y-3" style={{ minHeight: `${destinationStackHeightPx}px` }}>
                      {connectionEntries.map(entry => (
                        <MudMapRoom
                          key={entry.scene.id}
                          scene={entry.scene}
                          label={entry.label}
                          description={entry.summary}
                          compact
                          isInteractive={canTravel}
                          isSelected={pendingScene?.scene.id === entry.scene.id}
                          onClick={() => handleSceneSelect(entry)}
                        />
                      ))}
                    </div>
                  </div>
                </div>

                <div className="hidden md:block">
                  <div className="grid grid-cols-[minmax(0,12rem)_4rem_minmax(0,1fr)] items-start gap-4">
                    <div className="w-full max-w-[9rem] justify-self-start" style={{ minHeight: `${destinationStackHeightPx}px` }}>
                      <MudMapRoom scene={currentScenePreset} label="当前位置" compact />
                    </div>
                    <div className="relative" style={{ minHeight: `${destinationStackHeightPx}px` }}>
                      {connectionEntries.length > 0 && (
                        <svg className="absolute inset-0 h-full w-full" preserveAspectRatio="none">
                          {connectionEntries.map((entry, index) => (
                            <line
                              key={`connector-desktop-${entry.scene.id}`}
                              x1="0"
                              y1={`${getMapDestinationCenterPercent(0, connectionEntries.length)}%`}
                              x2="100%"
                              y2={`${getMapDestinationCenterPercent(index, connectionEntries.length)}%`}
                              stroke="rgba(74, 222, 128, 0.35)"
                              strokeWidth="1.5"
                            />
                          ))}
                        </svg>
                      )}
                    </div>
                    <div className="space-y-3" style={{ minHeight: `${destinationStackHeightPx}px` }}>
                      {connectionEntries.map(entry => (
                        <MudMapRoom
                          key={entry.scene.id}
                          scene={entry.scene}
                          label={entry.label}
                          description={entry.summary}
                          isInteractive={canTravel}
                          isSelected={pendingScene?.scene.id === entry.scene.id}
                          onClick={() => handleSceneSelect(entry)}
                        />
                      ))}
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </motion.div>

          <AnimatePresence>
            {pendingScene && (
              <motion.div
                initial={{ opacity: 0 }}
                animate={{ opacity: 1 }}
                exit={{ opacity: 0 }}
                className="map-modal-overlay fixed inset-0 z-[60] flex items-center justify-center bg-black/45 p-4 backdrop-blur-[2px]"
                onClick={event => {
                  event.stopPropagation();
                  setPendingScene(null);
                }}
              >
                <motion.div
                  initial={{ opacity: 0, scale: 0.96, y: 10 }}
                  animate={{ opacity: 1, scale: 1, y: 0 }}
                  exit={{ opacity: 0, scale: 0.96, y: 10 }}
                  transition={{ duration: 0.18, ease: 'easeOut' }}
                  className="platform-remap-surface map-modal-shell pixel-nine-slice pixel-modal-shell flex max-h-[min(92vh,36rem)] w-full max-w-md flex-col overflow-hidden shadow-[0_24px_80px_rgba(0,0,0,0.6)]"
                  style={getNineSliceStyle(UI_CHROME.modalPanel)}
                  onClick={event => event.stopPropagation()}
                >
                  <div className="relative border-b border-white/10 px-4 py-3 sm:px-5 sm:py-4">
                    <div className="min-w-0 pr-10">
                      <div className="text-[10px] tracking-[0.22em] text-amber-200/80">场景切换</div>
                       <div className="mt-1 truncate text-sm font-semibold text-white">{pendingScene.scene.name}</div>
                    </div>
                    <PixelCloseButton
                      onClick={() => setPendingScene(null)}
                      label="关闭场景切换"
                    />
                  </div>

                  <div className="min-h-0 flex-1 space-y-4 overflow-y-auto p-4 sm:p-5">
                    <PlatformSubpanel
                      as="div"
                      data-testid="map-target-scene-panel"
                      surface="darkAmber"
                      radius="xs"
                      padding="md"
                    >
                      <div className="text-[10px] tracking-[0.18em] text-amber-200/75">目标场景</div>
                      <div className="mt-2 text-base font-semibold text-white">{pendingScene.scene.name}</div>
                      <PlatformPillBadge
                        tone="darkAmber"
                        size="xxs"
                        className="mt-2 tracking-[0.18em]"
                      >
                        {pendingScene.label}
                      </PlatformPillBadge>
                      <div className="mt-2 text-sm leading-relaxed text-zinc-300">{pendingScene.scene.description}</div>
                      {pendingScene.summary ? (
                        <div className="mt-2 text-xs leading-6 text-zinc-400">
                          连接说明：{pendingScene.summary}
                        </div>
                      ) : null}
                    </PlatformSubpanel>

                    <div className="grid gap-3 sm:grid-cols-2">
                      <PlatformSubpanel
                        as="div"
                        data-testid="map-current-scene-summary"
                        surface="dark"
                        radius="xs"
                        padding="md"
                      >
                        <div className="text-[10px] tracking-[0.18em] text-zinc-500">当前</div>
                        <div className="mt-2 text-sm font-semibold text-white">{currentScenePreset.name}</div>
                      </PlatformSubpanel>
                      <PlatformSubpanel
                        as="div"
                        data-testid="map-next-scene-summary"
                        surface="dark"
                        radius="xs"
                        padding="md"
                      >
                        <div className="text-[10px] tracking-[0.18em] text-zinc-500">前往</div>
                        <div className="mt-2 text-sm font-semibold text-white">{pendingScene.scene.name}</div>
                      </PlatformSubpanel>
                    </div>

                    <div className="flex justify-end gap-2">
                      <PlatformActionButton
                        surface="editorDark"
                        tone="ghost"
                        size="xs"
                        onClick={() => setPendingScene(null)}
                      >
                        取消
                      </PlatformActionButton>
                      <PlatformActionButton
                        surface="editorDark"
                        tone={isTraveling || !canTravel ? 'ghost' : 'warning'}
                        size="xs"
                        disabled={isTraveling || !canTravel}
                        onClick={confirmTravel}
                      >
                        {isTraveling ? '切换中...' : canTravel ? '确认前往' : '当前不可切换'}
                      </PlatformActionButton>
                    </div>
                  </div>
                </motion.div>
              </motion.div>
            )}
          </AnimatePresence>
        </motion.div>
      )}
    </AnimatePresence>
  );
}
