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

import type { BarkBattleDraftConfig } from '../../../packages/shared/src/contracts/barkBattle';
import type {
  BarkBattleAssetSlot,
  BarkBattleGeneratedImageAssets,
  BarkBattleImageGenerationBatchResult,
  BarkBattleImageGenerationFailures,
} from '../../services/bark-battle-creation';
import {
  generateAllBarkBattleImageAssets,
  updateBarkBattleDraftConfig,
} from '../../services/bark-battle-creation';
import { PlatformPillBadge } from '../common/PlatformPillBadge';
import {
  GenerationCurrentStepCard,
  GenerationHeaderBackButton,
  GenerationPageBackdrop,
  GenerationProgressHero,
} from '../GenerationProgressHero';

type BarkBattleGeneratingViewProps = {
  draft: BarkBattleDraftConfig;
  isBusy?: boolean;
  error?: string | null;
  onBack: () => void;
  onComplete: (draft: BarkBattleDraftConfig, partialFailed: boolean) => void;
  onError: (message: string | null) => void;
};

type BarkBattleGeneratingSlotStatus = 'generating' | 'ready' | 'failed';

const GENERATION_STEPS = [
  { slot: 'player-character', label: '玩家形象' },
  { slot: 'opponent-character', label: '对手形象' },
  { slot: 'ui-background', label: '竞技背景' },
] as const satisfies readonly {
  slot: BarkBattleAssetSlot;
  label: string;
}[];

const activeBarkBattleGenerationTasks = new Map<
  string,
  Promise<BarkBattleImageGenerationBatchResult>
>();

function applyGeneratedAssets(
  draft: BarkBattleDraftConfig,
  assets: BarkBattleGeneratedImageAssets,
): BarkBattleDraftConfig {
  const nextDraft: BarkBattleDraftConfig = {
    ...draft,
    updatedAt: new Date().toISOString(),
  };

  if (assets['player-character']?.imageSrc) {
    nextDraft.playerCharacterImageSrc = assets['player-character'].imageSrc;
  }
  if (assets['opponent-character']?.imageSrc) {
    nextDraft.opponentCharacterImageSrc = assets['opponent-character'].imageSrc;
  }
  if (assets['ui-background']?.imageSrc) {
    nextDraft.uiBackgroundImageSrc = assets['ui-background'].imageSrc;
  }

  return nextDraft;
}

function hasSlotAsset(draft: BarkBattleDraftConfig, slot: BarkBattleAssetSlot) {
  if (slot === 'player-character') {
    return Boolean(draft.playerCharacterImageSrc?.trim());
  }
  if (slot === 'opponent-character') {
    return Boolean(draft.opponentCharacterImageSrc?.trim());
  }
  return Boolean(draft.uiBackgroundImageSrc?.trim());
}

function mergeSlotAsset(
  draft: BarkBattleDraftConfig,
  slot: BarkBattleAssetSlot,
  imageSrc: string,
): BarkBattleDraftConfig {
  if (slot === 'player-character') {
    return { ...draft, playerCharacterImageSrc: imageSrc };
  }
  if (slot === 'opponent-character') {
    return { ...draft, opponentCharacterImageSrc: imageSrc };
  }
  return { ...draft, uiBackgroundImageSrc: imageSrc };
}

function isDraftPersistable(draft: BarkBattleDraftConfig) {
  return Boolean(draft.draftId?.trim() && draft.workId?.trim());
}

function resolvePrimaryFailureMessage(
  failures: BarkBattleImageGenerationFailures,
) {
  for (const step of GENERATION_STEPS) {
    const message = failures[step.slot]?.trim();
    if (message) {
      return message;
    }
  }
  return null;
}

function buildDraftGenerationKey(draft: BarkBattleDraftConfig) {
  return [
    draft.draftId,
    draft.playerCharacterImageSrc ?? '',
    draft.opponentCharacterImageSrc ?? '',
    draft.uiBackgroundImageSrc ?? '',
  ].join('|');
}

function getSlotStatusLabel(status: BarkBattleGeneratingSlotStatus) {
  if (status === 'ready') {
    return '完成';
  }
  if (status === 'failed') {
    return '失败';
  }
  return '进行中';
}

function formatGenerationDuration(ms: number) {
  const totalSeconds = Math.max(1, Math.ceil(Math.max(0, ms) / 1000));
  const minutes = Math.floor(totalSeconds / 60);
  const seconds = totalSeconds % 60;

  if (minutes <= 0) {
    return `${seconds} 秒`;
  }

  if (seconds === 0) {
    return `${minutes} 分钟`;
  }

  return `${minutes} 分 ${seconds} 秒`;
}

function resolveBarkBattleProgressValue(
  slotStatuses: Partial<
    Record<BarkBattleAssetSlot, BarkBattleGeneratingSlotStatus>
  >,
) {
  const readyCount = GENERATION_STEPS.filter(
    (step) => slotStatuses[step.slot] === 'ready',
  ).length;
  return Math.round((readyCount / GENERATION_STEPS.length) * 100);
}

function resolveCurrentBarkBattleStep(
  slotStatuses: Partial<
    Record<BarkBattleAssetSlot, BarkBattleGeneratingSlotStatus>
  >,
) {
  return (
    GENERATION_STEPS.find((step) => slotStatuses[step.slot] === 'generating') ??
    GENERATION_STEPS.find((step) => slotStatuses[step.slot] === 'failed') ??
    GENERATION_STEPS.find((step) => slotStatuses[step.slot] !== 'ready') ??
    GENERATION_STEPS[GENERATION_STEPS.length - 1]
  );
}

export function BarkBattleGeneratingView({
  draft,
  isBusy = false,
  error = null,
  onBack,
  onComplete,
  onError,
}: BarkBattleGeneratingViewProps) {
  const startedDraftIdRef = useRef<string | null>(null);
  const [slotFailures, setSlotFailures] =
    useState<BarkBattleImageGenerationFailures>({});
  const [previewDraft, setPreviewDraft] = useState(draft);
  const [slotStatuses, setSlotStatuses] = useState<
    Partial<Record<BarkBattleAssetSlot, BarkBattleGeneratingSlotStatus>>
  >({});
  const [elapsedMs, setElapsedMs] = useState(0);
  const primaryFailureMessage = useMemo(
    () => resolvePrimaryFailureMessage(slotFailures),
    [slotFailures],
  );
  const progressValue = resolveBarkBattleProgressValue(slotStatuses);
  const currentStep = resolveCurrentBarkBattleStep(slotStatuses);
  const currentStepStatus = currentStep
    ? (slotStatuses[currentStep.slot] ??
      (hasSlotAsset(previewDraft, currentStep.slot) ? 'ready' : 'generating'))
    : 'generating';
  const currentStepProgress =
    currentStepStatus === 'ready'
      ? 100
      : currentStepStatus === 'failed'
        ? 100
        : 36;
  const currentStepLabel = currentStep?.label ?? '竞技素材';
  const currentStepStatusLabel = getSlotStatusLabel(currentStepStatus);

  useEffect(() => {
    const startedAtMs = Date.now();
    const timerId = window.setInterval(() => {
      setElapsedMs(Date.now() - startedAtMs);
    }, 1000);
    setElapsedMs(0);

    return () => {
      window.clearInterval(timerId);
    };
  }, [draft.draftId]);

  useEffect(() => {
    setPreviewDraft(draft);
    setSlotStatuses(
      GENERATION_STEPS.reduce<
        Partial<Record<BarkBattleAssetSlot, BarkBattleGeneratingSlotStatus>>
      >((statuses, step) => {
        statuses[step.slot] = hasSlotAsset(draft, step.slot)
          ? 'ready'
          : 'generating';
        return statuses;
      }, {}),
    );
  }, [draft]);

  useEffect(() => {
    if (
      !draft.draftId ||
      (() => {
        const draftGenerationKey = buildDraftGenerationKey(draft);
        return startedDraftIdRef.current === draftGenerationKey;
      })()
    ) {
      return;
    }
    const startedDraftKey = buildDraftGenerationKey(draft);
    startedDraftIdRef.current = startedDraftKey;
    let cancelled = false;
    const generationTask = generateAllBarkBattleImageAssets({
      config: draft,
      draftId: draft.draftId,
      onSlotComplete: (slot, result) => {
        if (cancelled || startedDraftIdRef.current !== startedDraftKey) {
          return;
        }
        if (result.status === 'fulfilled') {
          setPreviewDraft((currentDraft) =>
            mergeSlotAsset(currentDraft, slot, result.asset.imageSrc),
          );
          setSlotStatuses((current) => ({ ...current, [slot]: 'ready' }));
          setSlotFailures((current) => {
            const next = { ...current };
            delete next[slot];
            return next;
          });
          return;
        }
        setSlotStatuses((current) => ({ ...current, [slot]: 'failed' }));
        setSlotFailures((current) => ({ ...current, [slot]: result.message }));
      },
    });
    activeBarkBattleGenerationTasks.set(startedDraftKey, generationTask);

    onError(null);
    setSlotFailures({});
    setPreviewDraft(draft);
    setSlotStatuses(
      GENERATION_STEPS.reduce<
        Partial<Record<BarkBattleAssetSlot, BarkBattleGeneratingSlotStatus>>
      >((statuses, step) => {
        statuses[step.slot] = hasSlotAsset(draft, step.slot)
          ? 'ready'
          : 'generating';
        return statuses;
      }, {}),
    );
    void generationTask
      .then(async ({ assets, failures }) => {
        if (cancelled) {
          return;
        }
        setSlotFailures(failures);
        const primaryMessage = resolvePrimaryFailureMessage(failures);
        if (primaryMessage) {
          onError(primaryMessage);
        }
        const generatedDraft = applyGeneratedAssets(draft, assets);
        const partialFailed = GENERATION_STEPS.some(
          (step) => !hasSlotAsset(generatedDraft, step.slot),
        );
        if (!isDraftPersistable(generatedDraft)) {
          onComplete(generatedDraft, partialFailed);
          return;
        }

        try {
          const persistedDraft = await updateBarkBattleDraftConfig({
            draftId: generatedDraft.draftId,
            workId: generatedDraft.workId,
            configVersion: generatedDraft.configVersion,
            rulesetVersion: generatedDraft.rulesetVersion,
            title: generatedDraft.title,
            description: generatedDraft.description,
            themeDescription: generatedDraft.themeDescription,
            playerImageDescription: generatedDraft.playerImageDescription,
            opponentImageDescription: generatedDraft.opponentImageDescription,
            onomatopoeia: generatedDraft.onomatopoeia,
            playerCharacterImageSrc: generatedDraft.playerCharacterImageSrc,
            opponentCharacterImageSrc: generatedDraft.opponentCharacterImageSrc,
            uiBackgroundImageSrc: generatedDraft.uiBackgroundImageSrc,
            difficultyPreset: generatedDraft.difficultyPreset,
          });
          const updatedDraft = applyGeneratedAssets(persistedDraft, assets);
          if (!cancelled) {
            onComplete(updatedDraft, partialFailed);
          }
        } catch (persistError) {
          if (cancelled) {
            return;
          }
          onError(
            persistError instanceof Error
              ? persistError.message
              : '汪汪声浪素材保存失败。',
          );
          onComplete(generatedDraft, true);
        }
      })
      .catch((generationError) => {
        if (cancelled) {
          return;
        }
        onError(
          generationError instanceof Error
            ? generationError.message
            : '汪汪声浪素材生成失败。',
        );
        onComplete(draft, true);
      })
      .finally(() => {
        if (
          activeBarkBattleGenerationTasks.get(startedDraftKey) ===
          generationTask
        ) {
          activeBarkBattleGenerationTasks.delete(startedDraftKey);
        }
      });

    return () => {
      cancelled = true;
      // 中文注释：离开生成页后不再全局复用同一 Promise，避免悬挂生成任务导致再次进入时一直转圈。
      if (
        activeBarkBattleGenerationTasks.get(startedDraftKey) === generationTask
      ) {
        activeBarkBattleGenerationTasks.delete(startedDraftKey);
      }
      if (startedDraftIdRef.current === startedDraftKey) {
        startedDraftIdRef.current = null;
      }
    };
  }, [draft, onComplete, onError]);

  return (
    <div className="relative isolate z-[1] -mx-3 -my-3 flex h-[calc(100%+1.5rem)] min-h-0 flex-col overflow-hidden bg-transparent px-4 pb-[max(1.25rem,env(safe-area-inset-bottom))] pt-4 text-[#3d1f10] sm:mx-0 sm:my-0 sm:h-full sm:rounded-[2rem] sm:px-5 sm:pt-5 xl:px-6 xl:pb-5 xl:pt-5">
      <GenerationPageBackdrop />
      <div className="relative z-30 mx-auto mb-4 flex w-full max-w-[48rem] shrink-0 items-center justify-between gap-3 sm:mb-5">
        <GenerationHeaderBackButton
          label="返回编辑"
          onClick={onBack}
          disabled={isBusy}
          disabledOpacity={0.45}
        />
        <PlatformPillBadge
          tone="warning"
          size="xs"
          className="px-3 py-1.5 tracking-[0.08em] shadow-[0_12px_30px_rgba(214,77,31,0.08)] backdrop-blur-md sm:px-4 sm:text-xs"
        >
          生成中
        </PlatformPillBadge>
      </div>

      <div
        className="relative z-10 mx-auto flex min-h-0 w-full max-w-[48rem] flex-1 flex-col overflow-y-auto overscroll-y-contain"
        style={{ WebkitOverflowScrolling: 'touch' }}
      >
        <section className="grid content-start gap-3 overflow-hidden px-0 pb-2 pt-0">
          <GenerationProgressHero
            title="汪汪声浪素材生成进度"
            phaseLabel={draft.title || '未命名声浪竞技场'}
            progressValue={progressValue}
            estimatedWaitText="3 分钟"
            elapsedText={formatGenerationDuration(elapsedMs)}
          />

          <div className="mt-5 sm:mt-[-0.15rem]">
            <GenerationCurrentStepCard
              label={currentStepLabel}
              statusLabel={currentStepStatusLabel}
              progressValue={currentStepProgress}
            />
          </div>

          {error || primaryFailureMessage ? (
            <div className="rounded-[1.4rem] border border-[#d88969]/35 bg-white/76 px-4 py-3 text-sm leading-6 text-[#a6402f] backdrop-blur-md">
              {error ?? primaryFailureMessage}
            </div>
          ) : null}
        </section>
      </div>
    </div>
  );
}

export default BarkBattleGeneratingView;
