import {
  ArrowLeftOutlined,
  CheckCircleFilled,
  CloseOutlined,
  TrophyOutlined,
} from '@ant-design/icons';
import { Button, Progress, Tag } from 'antd';
import { useEffect, useMemo, useState } from 'react';
import './photoPuzzleAppView.css';
import { PHOTO_PUZZLE_CATEGORY_LABELS, PHOTO_PUZZLE_IMAGES, type PhotoPuzzleCategory } from './photoPuzzleCatalog';

type PhotoPuzzleAppViewProps = {
  onBack: () => void;
  launchContext?: 'direct' | 'embedded';
};

type PuzzleLevel = '3x3' | '4x4' | '5x5' | '6x6';

type PuzzleShape = {
  cols: number;
  rows: number;
  label: string;
};

type MobileFlowStep = 'category' | 'photo' | 'play';

const PUZZLE_LEVELS: Record<PuzzleLevel, PuzzleShape> = {
  '3x3': { cols: 3, rows: 3, label: '3 x 3' },
  '4x4': { cols: 4, rows: 4, label: '4 x 4' },
  '5x5': { cols: 5, rows: 5, label: '5 x 5' },
  '6x6': { cols: 6, rows: 6, label: '6 x 6' },
};

function buildSolvedBoard(tileCount: number) {
  return Array.from({ length: tileCount }, (_, index) => index);
}

function isSolved(board: number[]) {
  return board.every((value, index) => value === index);
}

function isAdjacent(leftIndex: number, rightIndex: number, cols: number) {
  const leftRow = Math.floor(leftIndex / cols);
  const leftCol = leftIndex % cols;
  const rightRow = Math.floor(rightIndex / cols);
  const rightCol = rightIndex % cols;
  return Math.abs(leftRow - rightRow) + Math.abs(leftCol - rightCol) === 1;
}

function moveTile(board: number[], tileIndex: number, cols: number) {
  const blankValue = board.length - 1;
  const blankIndex = board.indexOf(blankValue);

  if (!isAdjacent(tileIndex, blankIndex, cols)) {
    return board;
  }

  const nextBoard = [...board];
  [nextBoard[tileIndex], nextBoard[blankIndex]] = [nextBoard[blankIndex], nextBoard[tileIndex]];
  return nextBoard;
}

function shuffleBoard(level: PuzzleLevel) {
  const { cols, rows } = PUZZLE_LEVELS[level];
  const tileCount = cols * rows;
  let board = buildSolvedBoard(tileCount);
  let previousBlankIndex = -1;

  for (let step = 0; step < tileCount * 36; step += 1) {
    const blankIndex = board.indexOf(tileCount - 1);
    const candidates = board
      .map((_, index) => index)
      .filter((index) => index !== previousBlankIndex && isAdjacent(index, blankIndex, cols));
    const nextTileIndex = candidates[Math.floor(Math.random() * candidates.length)] ?? candidates[0];
    previousBlankIndex = blankIndex;
    board = moveTile(board, nextTileIndex, cols);
  }

  return isSolved(board) ? moveTile(board, tileCount - 2, cols) : board;
}

function getTileBackgroundPosition(tileValue: number, cols: number, rows: number) {
  const tileRow = Math.floor(tileValue / cols);
  const tileCol = tileValue % cols;
  const x = cols === 1 ? 0 : (tileCol / (cols - 1)) * 100;
  const y = rows === 1 ? 0 : (tileRow / (rows - 1)) * 100;
  return `${x}% ${y}%`;
}

function toCssBackgroundImage(imageUrl: string) {
  return `url("${imageUrl.replace(/"/g, '\\"')}")`;
}

function getTileScale(level: PuzzleLevel) {
  if (level === '6x6') {
    return 0.74;
  }

  if (level === '5x5') {
    return 0.82;
  }

  if (level === '4x4') {
    return 0.9;
  }

  return 1;
}

export function PhotoPuzzleAppView({ onBack, launchContext = 'direct' }: PhotoPuzzleAppViewProps) {
  const [category, setCategory] = useState<'all' | PhotoPuzzleCategory>('all');
  const [level, setLevel] = useState<PuzzleLevel>('4x4');
  const [selectedImageId, setSelectedImageId] = useState(PHOTO_PUZZLE_IMAGES[0]?.id ?? '');
  const [board, setBoard] = useState<number[]>(() => shuffleBoard('4x4'));
  const [moveCount, setMoveCount] = useState(0);
  const [completedAtMoves, setCompletedAtMoves] = useState<number | null>(null);
  const [galleryPage, setGalleryPage] = useState(0);
  const [isCompactViewport, setIsCompactViewport] = useState(() =>
    typeof window === 'undefined' ? false : window.matchMedia('(max-width: 720px)').matches,
  );
  const [isDesktopGalleryOpen, setIsDesktopGalleryOpen] = useState(false);
  const [mobileFlowStep, setMobileFlowStep] = useState<MobileFlowStep>('play');

  const filteredImages = useMemo(
    () => PHOTO_PUZZLE_IMAGES.filter((image) => category === 'all' || image.category === category),
    [category],
  );

  const selectedImage = useMemo(
    () => filteredImages.find((image) => image.id === selectedImageId) ?? PHOTO_PUZZLE_IMAGES.find((image) => image.id === selectedImageId) ?? PHOTO_PUZZLE_IMAGES[0],
    [filteredImages, selectedImageId],
  );

  const { cols, rows, label } = PUZZLE_LEVELS[level];
  const tileCount = cols * rows;
  const blankValue = tileCount - 1;
  const completedTileCount = board.filter((value, index) => value === index).length;
  const solvedPercent = Math.round((completedTileCount / tileCount) * 100);
  const galleryPageSize = 4;
  const galleryPageCount = Math.max(1, Math.ceil(filteredImages.length / galleryPageSize));
  const pagedImages = filteredImages.slice(galleryPage * galleryPageSize, galleryPage * galleryPageSize + galleryPageSize);
  const boardScale = getTileScale(level);
  const showDesktopGallery = !isCompactViewport && isDesktopGalleryOpen;
  const showCategorySelector = isCompactViewport && mobileFlowStep === 'category';
  const showPhotoSelector = isCompactViewport && mobileFlowStep === 'photo';
  const showPlayStage = !isCompactViewport || mobileFlowStep === 'play';
  const showMobileSelectionBar = isCompactViewport;
  const showMobileProgress = isCompactViewport && showPlayStage;
  const isEmbeddedLaunch = launchContext === 'embedded';

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

    if (!filteredImages.some((image) => image.id === selectedImage.id)) {
      setSelectedImageId(filteredImages[0]?.id ?? selectedImage.id);
    }
  }, [filteredImages, selectedImage]);

  useEffect(() => {
    setBoard(shuffleBoard(level));
    setMoveCount(0);
    setCompletedAtMoves(null);
  }, [level, selectedImageId]);

  useEffect(() => {
    const selectedIndex = filteredImages.findIndex((image) => image.id === selectedImage?.id);

    if (selectedIndex >= 0) {
      setGalleryPage(Math.floor(selectedIndex / galleryPageSize));
      return;
    }

    setGalleryPage(0);
  }, [filteredImages, selectedImage]);

  useEffect(() => {
    setGalleryPage((currentPage) => Math.min(currentPage, galleryPageCount - 1));
  }, [galleryPageCount]);

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

    const mediaQuery = window.matchMedia('(max-width: 720px)');
    const handleChange = () => {
      setIsCompactViewport(mediaQuery.matches);
    };

    handleChange();
    mediaQuery.addEventListener('change', handleChange);

    return () => {
      mediaQuery.removeEventListener('change', handleChange);
    };
  }, []);

  useEffect(() => {
    if (!isCompactViewport) {
      setMobileFlowStep('play');
      return;
    }

    setMobileFlowStep((currentStep) => currentStep);
  }, [isCompactViewport]);

  useEffect(() => {
    if (isCompactViewport) {
      setIsDesktopGalleryOpen(false);
    }
  }, [isCompactViewport]);

  const handleTileClick = (tileIndex: number) => {
    if (completedAtMoves !== null) {
      return;
    }

    const nextBoard = moveTile(board, tileIndex, cols);

    if (nextBoard === board) {
      return;
    }

    const nextMoveCount = moveCount + 1;
    setBoard(nextBoard);
    setMoveCount(nextMoveCount);

    if (isSolved(nextBoard)) {
      setCompletedAtMoves(nextMoveCount);
    }
  };

  const handleShuffle = () => {
    setBoard(shuffleBoard(level));
    setMoveCount(0);
    setCompletedAtMoves(null);
  };

  const handleReset = () => {
    setBoard(buildSolvedBoard(tileCount));
    setMoveCount(0);
    setCompletedAtMoves(null);
  };

  const handleRandomImage = () => {
    const candidates = filteredImages.length ? filteredImages : PHOTO_PUZZLE_IMAGES;
    const nextImage = candidates[Math.floor(Math.random() * candidates.length)] ?? PHOTO_PUZZLE_IMAGES[0];

    if (nextImage) {
      setSelectedImageId(nextImage.id);

      if (!isCompactViewport) {
        setIsDesktopGalleryOpen(false);
      }

      if (isCompactViewport) {
        setMobileFlowStep('play');
      }
    }
  };

  const handleCategorySelect = (value: 'all' | PhotoPuzzleCategory) => {
    setCategory(value);
    setGalleryPage(0);

    if (isCompactViewport) {
      setMobileFlowStep('photo');
    }
  };

  const handleImageSelect = (imageId: string) => {
    setSelectedImageId(imageId);

    if (isCompactViewport) {
      setMobileFlowStep('play');
      return;
    }

    setIsDesktopGalleryOpen(false);
  };

  if (!selectedImage) {
    return null;
  }

  return (
    <section className="photo-puzzle-app" data-testid="photo-puzzle-app" data-level={level}>
      <header className="photo-puzzle-app__hero">
        <div className="photo-puzzle-app__hero-copy">
          <div className="photo-puzzle-app__hero-nav">
            <Button type="text" icon={<ArrowLeftOutlined />} className="photo-puzzle-app__back" onClick={onBack}>
              앱 보관함
            </Button>
            {isEmbeddedLaunch ? (
              <Button type="text" icon={<CloseOutlined />} className="photo-puzzle-app__exit" onClick={onBack}>
                종료
              </Button>
            ) : null}
          </div>
          <div className="photo-puzzle-app__eyebrow">
            <Tag bordered={false} color="geekblue">
              Photo Puzzle
            </Tag>
            <span>Play / Apps / Apps</span>
          </div>
        </div>

        <div className="photo-puzzle-app__hero-stats">
          <div className="photo-puzzle-app__hero-stat photo-puzzle-app__hero-stat--photo">
            <span>사진</span>
            <strong>{PHOTO_PUZZLE_IMAGES.length}장</strong>
          </div>
          <div className="photo-puzzle-app__hero-stat photo-puzzle-app__hero-stat--level">
            <span>난이도</span>
            <strong>{label}</strong>
          </div>
          <div className="photo-puzzle-app__hero-stat photo-puzzle-app__hero-stat--moves">
            <span>이동</span>
            <strong>{moveCount}회</strong>
          </div>
        </div>
      </header>

      <div className={`photo-puzzle-app__body${showDesktopGallery ? ' photo-puzzle-app__body--with-gallery' : ''}`}>
        <div className={`photo-puzzle-app__board-panel${showPlayStage ? ' photo-puzzle-app__board-panel--play' : ''}`}>
          {showMobileSelectionBar ? (
            <div className="photo-puzzle-app__mobile-selection-bar">
              <Button
                size="small"
                type="text"
                icon={<ArrowLeftOutlined />}
                className="photo-puzzle-app__mobile-back"
                onClick={onBack}
                aria-label="앱 보관함으로 돌아가기"
              />
              <img src={selectedImage.imageUrl} alt={selectedImage.title} className="photo-puzzle-app__mobile-selection-thumb" />
              <div className="photo-puzzle-app__mobile-selection-actions">
                <Button size="small" onClick={() => setMobileFlowStep('photo')}>
                  사진 고르기
                </Button>
                <Button size="small" onClick={() => setMobileFlowStep('category')}>
                  분류 보기
                </Button>
                {showPlayStage ? (
                  <Button size="small" onClick={handleShuffle} data-testid="photo-puzzle-shuffle">
                    셔플
                  </Button>
                ) : (
                  <Button size="small" onClick={() => setMobileFlowStep('play')}>
                    퍼즐 보기
                  </Button>
                )}
              </div>
            </div>
          ) : null}

          {showCategorySelector ? (
            <div className="photo-puzzle-app__category-panel">
              <div className="photo-puzzle-app__category-group" role="group" aria-label="사진 카테고리 선택">
                {(Object.keys(PHOTO_PUZZLE_CATEGORY_LABELS) as Array<'all' | PhotoPuzzleCategory>).map((value) => (
                  <button
                    key={value}
                    type="button"
                    className={`photo-puzzle-app__chip photo-puzzle-app__chip--small${value === category ? ' photo-puzzle-app__chip--active' : ''}`}
                    onClick={() => handleCategorySelect(value)}
                  >
                    {PHOTO_PUZZLE_CATEGORY_LABELS[value]}
                  </button>
                ))}
              </div>
            </div>
          ) : null}

          {showPhotoSelector ? (
            <div className="photo-puzzle-app__mobile-photo-panel">
              <div className="photo-puzzle-app__gallery-grid">
                {pagedImages.map((image) => {
                  const isActive = image.id === selectedImage.id;

                  return (
                    <button
                      key={image.id}
                      type="button"
                      className={`photo-puzzle-app__thumb${isActive ? ' photo-puzzle-app__thumb--active' : ''}`}
                      onClick={() => handleImageSelect(image.id)}
                      data-testid={`photo-puzzle-image-${image.id}`}
                    >
                      <img src={image.imageUrl} alt={image.title} />
                      {isCompactViewport ? null : (
                        <span className="photo-puzzle-app__thumb-meta">
                          <Tag bordered={false}>{image.badge}</Tag>
                          <strong>{image.title}</strong>
                        </span>
                      )}
                    </button>
                  );
                })}
              </div>
              <div className="photo-puzzle-app__gallery-pager">
                <div className="photo-puzzle-app__gallery-pager-count">
                  <strong>
                    {galleryPage + 1} / {galleryPageCount}
                  </strong>
                </div>
                <div className="photo-puzzle-app__gallery-pager-actions">
                  <Button disabled={galleryPage === 0} onClick={() => setGalleryPage((currentPage) => Math.max(0, currentPage - 1))}>
                    이전
                  </Button>
                  <Button disabled={galleryPage >= galleryPageCount - 1} onClick={() => setGalleryPage((currentPage) => Math.min(galleryPageCount - 1, currentPage + 1))}>
                    다음
                  </Button>
                </div>
              </div>
            </div>
          ) : null}

          {showPlayStage ? (
            <>
              {showMobileProgress ? null : (
                <>
                  <div className="photo-puzzle-app__control-strip">
                    <div className="photo-puzzle-app__level-group" role="group" aria-label="퍼즐 크기 선택">
                      {(Object.keys(PUZZLE_LEVELS) as PuzzleLevel[]).map((value) => (
                        <button
                          key={value}
                          type="button"
                          className={`photo-puzzle-app__chip${value === level ? ' photo-puzzle-app__chip--active' : ''}`}
                          onClick={() => setLevel(value)}
                          data-testid={`photo-puzzle-level-${value}`}
                        >
                          {PUZZLE_LEVELS[value].label}
                        </button>
                      ))}
                    </div>

                    <div className="photo-puzzle-app__toolbar-actions">
                      <Button onClick={() => setIsDesktopGalleryOpen((currentValue) => !currentValue)}>
                        {showDesktopGallery ? '1 닫기' : '1 사진'}
                      </Button>
                      <Button onClick={handleRandomImage} data-testid="photo-puzzle-random-image">
                        2 변경
                      </Button>
                      <Button onClick={handleShuffle} data-testid="photo-puzzle-shuffle">
                        3 셔플
                      </Button>
                      <Button onClick={handleReset} data-testid="photo-puzzle-reset">
                        4 원본
                      </Button>
                    </div>
                  </div>

                  <div className="photo-puzzle-app__status-row">
                    <div className="photo-puzzle-app__status-card">
                      <span>완성도</span>
                      <strong>{solvedPercent}%</strong>
                      <Progress percent={solvedPercent} showInfo={false} strokeColor="#1d4ed8" trailColor="rgba(148, 163, 184, 0.18)" />
                    </div>
                    <div className="photo-puzzle-app__status-card">
                      <span>선택 사진</span>
                      <strong>{selectedImage.title}</strong>
                      <Tag bordered={false}>{selectedImage.badge}</Tag>
                    </div>
                    <div className="photo-puzzle-app__status-card">
                      <span>퍼즐 상태</span>
                      <strong>{completedAtMoves === null ? '진행 중' : '완료'}</strong>
                      {completedAtMoves === null ? (
                        <Tag bordered={false} color="processing">
                          {moveCount}회 이동
                        </Tag>
                      ) : (
                        <Tag bordered={false} color="success" icon={<CheckCircleFilled />}>
                          {completedAtMoves} 완료
                        </Tag>
                      )}
                    </div>
                  </div>
                </>
              )}

              <div className="photo-puzzle-app__board-stage">
                <div className="photo-puzzle-app__board-frame" style={{ ['--photo-puzzle-board-scale' as string]: `${boardScale}` }}>
                  <div
                    className="photo-puzzle-app__board"
                    data-testid="photo-puzzle-board"
                    data-board-signature={board.join(',')}
                    data-board-size={tileCount}
                    data-move-count={moveCount}
                    style={{ gridTemplateColumns: `repeat(${cols}, 1fr)` }}
                  >
                    {board.map((tileValue, tileIndex) => {
                      const isBlank = tileValue === blankValue;
                      return (
                        <button
                          key={`${tileValue}-${tileIndex}`}
                          type="button"
                          className={`photo-puzzle-app__tile${isBlank ? ' photo-puzzle-app__tile--blank' : ''}`}
                          disabled={isBlank}
                          data-testid={isBlank ? 'photo-puzzle-blank-tile' : `photo-puzzle-tile-${tileValue}`}
                          data-tile-index={tileIndex}
                          data-tile-value={tileValue}
                          onClick={() => handleTileClick(tileIndex)}
                          aria-label={isBlank ? '빈 칸' : '퍼즐 조각 이동'}
                          style={
                            isBlank
                              ? undefined
                              : {
                                  backgroundImage: toCssBackgroundImage(selectedImage.imageUrl),
                                  backgroundSize: `${cols * 100}% ${rows * 100}%`,
                                  backgroundPosition: getTileBackgroundPosition(tileValue, cols, rows),
                                }
                          }
                        />
                      );
                    })}

                    {completedAtMoves === null ? null : (
                      <div className="photo-puzzle-app__success">
                        <TrophyOutlined />
                        <strong>퍼즐 완료</strong>
                        <span>{completedAtMoves}번 만에 맞췄습니다.</span>
                        <Button type="primary" onClick={handleShuffle}>
                          다시 섞기
                        </Button>
                      </div>
                    )}
                  </div>
                </div>
              </div>

              {isCompactViewport ? (
                <>
                  <div className="photo-puzzle-app__mobile-progress" data-testid="photo-puzzle-mobile-progress">
                    <div className="photo-puzzle-app__mobile-progress-copy">
                      <span>완성도</span>
                      <strong>{solvedPercent}%</strong>
                    </div>
                    <Progress
                      percent={solvedPercent}
                      showInfo={false}
                      size="small"
                      strokeColor="#1d4ed8"
                      trailColor="rgba(148, 163, 184, 0.22)"
                    />
                    {completedAtMoves === null ? (
                      <Tag bordered={false} color="processing">
                        진행 중
                      </Tag>
                    ) : (
                      <Tag bordered={false} color="success" icon={<CheckCircleFilled />}>
                        {completedAtMoves}회 완료
                      </Tag>
                    )}
                  </div>
                  <div className="photo-puzzle-app__level-group photo-puzzle-app__level-group--mobile" role="group" aria-label="퍼즐 크기 선택">
                    {(Object.keys(PUZZLE_LEVELS) as PuzzleLevel[]).map((value) => (
                      <button
                        key={value}
                        type="button"
                        className={`photo-puzzle-app__chip photo-puzzle-app__chip--small${value === level ? ' photo-puzzle-app__chip--active' : ''}`}
                        onClick={() => setLevel(value)}
                        data-testid={`photo-puzzle-level-${value}`}
                      >
                        {PUZZLE_LEVELS[value].label}
                      </button>
                    ))}
                  </div>
                  <div className="photo-puzzle-app__mobile-play-actions">
                    <Button onClick={handleReset} data-testid="photo-puzzle-reset">
                      원본
                    </Button>
                    <Button onClick={handleRandomImage} data-testid="photo-puzzle-random-image">
                      랜덤 사진
                    </Button>
                  </div>
                </>
              ) : null}
            </>
          ) : null}
        </div>

        {showDesktopGallery ? (
          <aside className="photo-puzzle-app__gallery-panel">
            <div className="photo-puzzle-app__gallery-top">
              <div className="photo-puzzle-app__category-group" role="group" aria-label="사진 카테고리 선택">
                {(Object.keys(PHOTO_PUZZLE_CATEGORY_LABELS) as Array<'all' | PhotoPuzzleCategory>).map((value) => (
                  <button
                    key={value}
                    type="button"
                    className={`photo-puzzle-app__chip photo-puzzle-app__chip--small${value === category ? ' photo-puzzle-app__chip--active' : ''}`}
                    onClick={() => handleCategorySelect(value)}
                  >
                    {PHOTO_PUZZLE_CATEGORY_LABELS[value]}
                  </button>
                ))}
              </div>
            </div>

            <div className="photo-puzzle-app__selected-preview">
              <img src={selectedImage.imageUrl} alt={selectedImage.title} />
              <div className="photo-puzzle-app__selected-preview-copy">
                <Tag bordered={false}>{selectedImage.badge}</Tag>
                <strong>{selectedImage.title}</strong>
              </div>
            </div>

            <div className="photo-puzzle-app__gallery-grid">
              {pagedImages.map((image) => {
                const isActive = image.id === selectedImage.id;

                return (
                  <button
                    key={image.id}
                    type="button"
                    className={`photo-puzzle-app__thumb${isActive ? ' photo-puzzle-app__thumb--active' : ''}`}
                    onClick={() => handleImageSelect(image.id)}
                    data-testid={`photo-puzzle-image-${image.id}`}
                  >
                    <img src={image.imageUrl} alt={image.title} />
                    {isCompactViewport ? null : (
                      <span className="photo-puzzle-app__thumb-meta">
                        <Tag bordered={false}>{image.badge}</Tag>
                        <strong>{image.title}</strong>
                      </span>
                    )}
                  </button>
                );
              })}
            </div>

            <div className="photo-puzzle-app__gallery-pager">
              <div className="photo-puzzle-app__gallery-pager-count">
                <strong>
                  {galleryPage + 1} / {galleryPageCount}
                </strong>
              </div>
              <div className="photo-puzzle-app__gallery-pager-actions">
                <Button disabled={galleryPage === 0} onClick={() => setGalleryPage((currentPage) => Math.max(0, currentPage - 1))}>
                  이전
                </Button>
                <Button disabled={galleryPage >= galleryPageCount - 1} onClick={() => setGalleryPage((currentPage) => Math.min(galleryPageCount - 1, currentPage + 1))}>
                  다음
                </Button>
              </div>
            </div>
          </aside>
        ) : null}
      </div>
    </section>
  );
}
