import {
  CopyOutlined,
  DownloadOutlined,
  DownOutlined,
  FileImageOutlined,
  FileTextOutlined,
  FullscreenExitOutlined,
  FullscreenOutlined,
  RightOutlined,
} from '@ant-design/icons';
import { Button, Empty, Segmented, Space, Tag, Typography, message } from 'antd';
import { useEffect, useMemo, useState } from 'react';
import { copyTextToClipboard } from '../../utils/clipboard';
import {
  CODEX_DIFF_STATUS_LABEL_MAP,
  CodexDiffBlock,
  type CodexDiffFileStatus,
} from './CodexDiffBlock';
import { PreviewerUI } from './PreviewerUI';
import './CodexDiffPreviewer.css';

const { Text } = Typography;

export type CodexDiffPreviewerFileStatus = CodexDiffFileStatus;

export type CodexDiffPreviewerFile = {
  path: string;
  previousPath: string | null;
  status: CodexDiffPreviewerFileStatus;
  previewType?: 'code' | 'image' | 'empty';
  language: string;
  content: string;
};

type CodexDiffPreviewerProps = {
  title?: string;
  description?: string;
  files: CodexDiffPreviewerFile[];
  diffText?: string | null;
  height?: number | string;
  mode?: 'auto' | 'source' | 'diff';
  showModeSwitch?: boolean;
};

function buildStatusCount(files: CodexDiffPreviewerFile[]) {
  return files.reduce<Record<CodexDiffPreviewerFile['status'], number>>(
    (counts, file) => ({
      ...counts,
      [file.status]: (counts[file.status] ?? 0) + 1,
    }),
    {
      added: 0,
      modified: 0,
      deleted: 0,
      renamed: 0,
      binary: 0,
      unknown: 0,
    },
  );
}

function resolvePreviewType(file: CodexDiffPreviewerFile) {
  return file.previewType ?? 'code';
}

function resolveTagColor(status: CodexDiffPreviewerFileStatus) {
  if (status === 'added') {
    return 'green';
  }

  if (status === 'modified') {
    return 'blue';
  }

  if (status === 'deleted') {
    return 'red';
  }

  if (status === 'renamed') {
    return 'gold';
  }

  return 'default';
}

function buildExpandedPathSet(paths: string[]) {
  return Array.from(new Set(paths));
}

export function CodexDiffPreviewer({
  title = 'Codex Diff Preview',
  description = '변경 및 신규 소스 전체 미리보기',
  files,
  diffText,
  height = 'calc(100vh - 280px)',
  mode = 'auto',
  showModeSwitch = true,
}: CodexDiffPreviewerProps) {
  const [messageApi, contextHolder] = message.useMessage();
  const [activeMode, setActiveMode] = useState<'source' | 'diff'>(files.length > 0 ? 'source' : 'diff');
  const [expandedSourcePaths, setExpandedSourcePaths] = useState<string[]>(() =>
    files[0]?.path ? [files[0].path] : [],
  );
  const [expandedPreviewPath, setExpandedPreviewPath] = useState<string | null>(null);
  const statusCount = useMemo(() => buildStatusCount(files), [files]);
  const canShowSource = files.length > 0;
  const canShowDiff = Boolean(diffText);
  const resolvedMode = mode === 'auto' ? activeMode : mode;

  async function handleCopy(content: string) {
    try {
      await copyTextToClipboard(content);
      messageApi.success('복사했습니다.');
    } catch {
      messageApi.error('복사에 실패했습니다.');
    }
  }

  function handleDownload(path: string, content: string) {
    if (typeof document === 'undefined') {
      messageApi.error('다운로드를 사용할 수 없습니다.');
      return;
    }

    const fileName = path.split('/').pop() || 'preview.txt';
    const blob = new Blob([content], { type: 'text/plain;charset=utf-8' });
    const objectUrl = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = objectUrl;
    link.download = fileName;
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
    URL.revokeObjectURL(objectUrl);
  }

  function handleFullscreen(path: string) {
    setExpandedSourcePaths((currentPaths) => (currentPaths.includes(path) ? currentPaths : [...currentPaths, path]));
    setExpandedPreviewPath((currentPath) => (currentPath === path ? null : path));
  }

  useEffect(() => {
    if (!canShowSource) {
      if (diffText) {
        setActiveMode('diff');
      }
      return;
    }

    setExpandedSourcePaths((currentPaths) => {
      const nextPaths = currentPaths.filter((path) => files.some((file) => file.path === path));
      if (nextPaths.length > 0) {
        return buildExpandedPathSet(nextPaths);
      }

      return files[0]?.path ? [files[0].path] : [];
    });
  }, [canShowSource, diffText, files]);

  useEffect(() => {
    if (typeof document === 'undefined' || !expandedPreviewPath) {
      return;
    }

    const previousOverflow = document.body.style.overflow;
    const handleKeyDown = (event: KeyboardEvent) => {
      if (event.key === 'Escape') {
        setExpandedPreviewPath(null);
      }
    };

    document.body.style.overflow = 'hidden';
    document.addEventListener('keydown', handleKeyDown);

    return () => {
      document.body.style.overflow = previousOverflow;
      document.removeEventListener('keydown', handleKeyDown);
    };
  }, [expandedPreviewPath]);

  useEffect(() => {
    if (mode === 'source' && canShowSource) {
      setActiveMode('source');
      return;
    }

    if (mode === 'diff' && canShowDiff) {
      setActiveMode('diff');
    }
  }, [canShowDiff, canShowSource, mode]);

  if (!files.length && !diffText) {
    return <Empty description="기록된 소스 미리보기가 없습니다." />;
  }

  return (
    <>
      {contextHolder}
      <div className="codex-diff-previewer">
      <div className="codex-diff-previewer__header">
        <div>
          <div className="codex-diff-previewer__title">{title}</div>
          <Text type="secondary">{description}</Text>
        </div>
        <Space wrap size={[6, 6]} className="codex-diff-previewer__stats">
          {files.length ? <Tag bordered={false}>전체 {files.length}</Tag> : null}
          {statusCount.added ? <Tag color="green">{`신규 ${statusCount.added}`}</Tag> : null}
          {statusCount.modified ? <Tag color="blue">{`변경 ${statusCount.modified}`}</Tag> : null}
          {statusCount.renamed ? <Tag color="gold">{`이동 ${statusCount.renamed}`}</Tag> : null}
          {statusCount.deleted ? <Tag color="red">{`삭제 ${statusCount.deleted}`}</Tag> : null}
          {statusCount.binary ? <Tag>{`바이너리 ${statusCount.binary}`}</Tag> : null}
        </Space>
      </div>

      {showModeSwitch && canShowSource && canShowDiff ? (
        <Segmented
          value={activeMode}
          onChange={(value) => {
            setActiveMode(value as 'source' | 'diff');
          }}
          options={[
            { label: '전체소스', value: 'source' },
            { label: 'raw diff', value: 'diff' },
          ]}
        />
      ) : null}

      {resolvedMode === 'diff' && diffText ? (
        <CodexDiffBlock diffText={diffText} />
      ) : canShowSource ? (
        <div className="codex-diff-previewer__diff-list">
          <div className="codex-diff-previewer__diff-toolbar">
            <Text type="secondary">{`파일 ${files.length}개 기준으로 전체 소스를 분리해 표시합니다.`}</Text>
            <Space size={8} wrap>
              <Button
                size="small"
                onClick={() => {
                  setExpandedSourcePaths(files.map((file) => file.path));
                }}
              >
                전체 펼치기
              </Button>
              <Button
                size="small"
                onClick={() => {
                  setExpandedSourcePaths([]);
                }}
              >
                전체 접기
              </Button>
            </Space>
          </div>

          {files.map((file) => {
            const isExpanded = expandedSourcePaths.includes(file.path);
            const isPreviewExpanded = expandedPreviewPath === file.path;
            const displayPath = file.previousPath ? `${file.previousPath} -> ${file.path}` : file.path;

            return (
              <section
                className={[
                  'codex-diff-previewer__diff-section',
                  isPreviewExpanded ? 'codex-diff-previewer__diff-section--expanded' : '',
                ]
                  .filter(Boolean)
                  .join(' ')}
                key={file.path}
              >
                <button
                  type="button"
                  className="codex-diff-previewer__diff-toggle"
                  onClick={() => {
                    setExpandedSourcePaths((currentPaths) =>
                      currentPaths.includes(file.path)
                        ? currentPaths.filter((path) => path !== file.path)
                        : [...currentPaths, file.path],
                    );
                  }}
                >
                  <Space align="center" size={10} className="codex-diff-previewer__diff-toggle-main">
                    {isExpanded ? <DownOutlined /> : <RightOutlined />}
                    {resolvePreviewType(file) === 'image' ? <FileImageOutlined /> : <FileTextOutlined />}
                    <span className="codex-diff-previewer__file-name">{file.path.split('/').pop() ?? file.path}</span>
                    <Tag bordered={false} color={resolveTagColor(file.status)}>
                      {CODEX_DIFF_STATUS_LABEL_MAP[file.status]}
                    </Tag>
                  </Space>
                  <Text type="secondary" className="codex-diff-previewer__file-path">
                    {displayPath}
                  </Text>
                </button>
                <Space size={4} className="codex-diff-previewer__diff-actions">
                  <Button
                    size="small"
                    type="text"
                    aria-label="복사"
                    icon={<CopyOutlined />}
                    onClick={(event) => {
                      event.stopPropagation();
                      void handleCopy(file.content);
                    }}
                  />
                  <Button
                    size="small"
                    type="text"
                    aria-label="다운로드"
                    icon={<DownloadOutlined />}
                    onClick={(event) => {
                      event.stopPropagation();
                      handleDownload(file.path, file.content);
                    }}
                  />
                  <Button
                    size="small"
                    type="text"
                    aria-label={isPreviewExpanded ? '최대화 해제' : '최대화'}
                    icon={isPreviewExpanded ? <FullscreenExitOutlined /> : <FullscreenOutlined />}
                    onClick={(event) => {
                      event.stopPropagation();
                      handleFullscreen(file.path);
                    }}
                  />
                </Space>

                {isExpanded || isPreviewExpanded ? (
                  <div className="codex-diff-previewer__diff-body">
                    <PreviewerUI
                      type={resolvePreviewType(file)}
                      showHeader={false}
                      value={file.content}
                      language={file.language}
                      imageAlt={file.path.split('/').pop() ?? file.path}
                      downloadFileName={file.path.split('/').pop() ?? file.path}
                      height={isPreviewExpanded ? 'calc(100vh - 120px)' : height}
                      copyable={false}
                      maximizable={false}
                    />
                  </div>
                ) : null}
              </section>
            );
          })}
        </div>
      ) : (
        <Empty description="표시할 소스 파일이 없습니다." />
      )}
      </div>
    </>
  );
}
