/**
 * CheckpointDiffViewer Component
 *
 * 检查点差异查看器：显示检查点与当前工作区的差异
 */

import { useState, useEffect, useCallback } from 'react';
import {
  X,
  RefreshCw,
  ChevronDown,
  ChevronRight,
  Eye,
  Check,
  RotateCcw,
} from 'lucide-react';
import { motion, AnimatePresence } from 'framer-motion';
import { toast } from 'sonner';
import { cn } from '../utils/cn';
import { modalOverlay, modalContent, smoothTransition } from '../utils/animations';
import { Button } from '../primitives/Button';
import { Skeleton } from './Skeleton';
import {
  getCheckpoint,
  getCheckpointDiff,
  getFileDiff,
  type CheckpointDetail,
  type DiffInfo,
  type FileDiffDetail,
  type FileChangeType,
} from '../api/client.js';

interface CheckpointDiffViewerProps {
  /** 检查点 ID */
  checkpointId: string;
  /** 关闭回调 */
  onClose: () => void;
  /** 恢复选中文件 */
  onRestoreSelected?: (checkpointId: string, files: string[]) => void;
  /** 恢复全部 */
  onRestoreAll?: (checkpointId: string) => void;
  /** 是否启用响应式布局 */
  responsive?: boolean;
}

// 文件变更类型标签
function getChangeLabel(type: FileChangeType) {
  switch (type) {
    case 'added':
      return 'A';
    case 'modified':
      return 'M';
    case 'deleted':
      return 'D';
    case 'renamed':
      return 'R';
    default:
      return '?';
  }
}

// 文件变更类型颜色
function getChangeColor(type: FileChangeType) {
  switch (type) {
    case 'added':
      return 'text-green-400 bg-green-400/10';
    case 'modified':
      return 'text-yellow-400 bg-yellow-400/10';
    case 'deleted':
      return 'text-red-400 bg-red-400/10';
    case 'renamed':
      return 'text-blue-400 bg-blue-400/10';
    default:
      return 'text-fg-muted bg-surface-muted/10';
  }
}

export function CheckpointDiffViewer({
  checkpointId,
  onClose,
  onRestoreSelected,
  onRestoreAll,
  responsive = false,
}: CheckpointDiffViewerProps) {
  // 数据状态
  const [checkpoint, setCheckpoint] = useState<CheckpointDetail | null>(null);
  const [diff, setDiff] = useState<DiffInfo | null>(null);
  const [selectedFiles, setSelectedFiles] = useState<Set<string>>(new Set());
  const [expandedFile, setExpandedFile] = useState<string | null>(null);
  const [fileDiff, setFileDiff] = useState<FileDiffDetail | null>(null);

  // UI 状态
  const [loading, setLoading] = useState(true);
  const [refreshing, setRefreshing] = useState(false);
  const [loadingFileDiff, setLoadingFileDiff] = useState(false);

  // 加载数据
  const loadData = useCallback(async () => {
    try {
      const [cpResult, diffResult] = await Promise.all([
        getCheckpoint(checkpointId),
        getCheckpointDiff(checkpointId),
      ]);

      if (cpResult.success && cpResult.data) {
        setCheckpoint(cpResult.data);
      } else {
        toast.error(cpResult.error || 'Failed to load checkpoint');
      }

      if (diffResult.success && diffResult.data) {
        setDiff(diffResult.data);
        // 默认全选
        setSelectedFiles(new Set(diffResult.data.files.map((f) => f.path)));
      } else {
        toast.error(diffResult.error || 'Failed to load diff');
      }
    } catch (err) {
      toast.error(err instanceof Error ? err.message : 'Failed to load data');
    }
  }, [checkpointId]);

  // 初始加载
  useEffect(() => {
    setLoading(true);
    loadData().finally(() => setLoading(false));
  }, [loadData]);

  // 刷新
  const handleRefresh = async () => {
    setRefreshing(true);
    await loadData();
    setRefreshing(false);
    toast.success('Diff refreshed');
  };

  // 切换文件选择
  const toggleFileSelection = (path: string) => {
    const newSelected = new Set(selectedFiles);
    if (newSelected.has(path)) {
      newSelected.delete(path);
    } else {
      newSelected.add(path);
    }
    setSelectedFiles(newSelected);
  };

  // 全选/取消全选
  const toggleSelectAll = () => {
    if (diff) {
      if (selectedFiles.size === diff.files.length) {
        setSelectedFiles(new Set());
      } else {
        setSelectedFiles(new Set(diff.files.map((f) => f.path)));
      }
    }
  };

  // 查看文件差异
  const handleViewFileDiff = async (path: string) => {
    if (expandedFile === path) {
      setExpandedFile(null);
      setFileDiff(null);
      return;
    }

    setExpandedFile(path);
    setLoadingFileDiff(true);

    try {
      const result = await getFileDiff(checkpointId, path);
      if (result.success && result.data) {
        setFileDiff(result.data);
      } else {
        toast.error(result.error || 'Failed to load file diff');
        setFileDiff(null);
      }
    } catch (err) {
      toast.error(err instanceof Error ? err.message : 'Failed to load file diff');
      setFileDiff(null);
    } finally {
      setLoadingFileDiff(false);
    }
  };

  // 恢复选中文件
  const handleRestoreSelected = () => {
    if (onRestoreSelected && selectedFiles.size > 0) {
      onRestoreSelected(checkpointId, Array.from(selectedFiles));
    }
  };

  // 恢复全部
  const handleRestoreAll = () => {
    if (onRestoreAll) {
      onRestoreAll(checkpointId);
    }
  };

  // Loading 骨架屏
  const LoadingSkeleton = () => (
    <div className="space-y-3 p-4">
      <Skeleton className="h-6 w-64" />
      <div className="space-y-2">
        {[1, 2, 3, 4].map((i) => (
          <div key={i} className="flex items-center gap-3 p-2 bg-surface-base/50 rounded">
            <Skeleton className="h-4 w-4 rounded" />
            <Skeleton className="h-4 w-48" />
            <Skeleton className="h-3 w-16 ml-auto" />
          </div>
        ))}
      </div>
    </div>
  );

  // 渲染 diff 补丁
  const renderPatch = (patch: string) => {
    const lines = patch.split('\n');
    return (
      <pre className="text-xs font-mono overflow-x-auto">
        {lines.map((line, index) => {
          let className = 'px-2 py-0.5';
          if (line.startsWith('+') && !line.startsWith('+++')) {
            className += ' bg-green-500/10 text-green-400';
          } else if (line.startsWith('-') && !line.startsWith('---')) {
            className += ' bg-red-500/10 text-red-400';
          } else if (line.startsWith('@@')) {
            className += ' bg-blue-500/10 text-blue-400';
          } else {
            className += ' text-fg-muted';
          }
          return (
            <div key={index} className={className}>
              {line || ' '}
            </div>
          );
        })}
      </pre>
    );
  };

  return (
    <AnimatePresence>
      <motion.div
        variants={modalOverlay}
        initial="initial"
        animate="animate"
        exit="exit"
        transition={{ duration: 0.2 }}
        className={cn(
          'fixed inset-0 bg-black/50 flex z-50',
          responsive ? 'items-end md:items-center justify-center' : 'items-center justify-center'
        )}
        onClick={onClose}
      >
        <motion.div
          variants={modalContent}
          initial="initial"
          animate="animate"
          exit="exit"
          transition={smoothTransition}
          onClick={(e) => e.stopPropagation()}
          className={cn(
            'bg-surface-subtle max-h-[90vh] overflow-hidden flex flex-col',
            responsive
              ? 'w-full md:w-full md:max-w-3xl md:mx-4 rounded-t-2xl md:rounded-lg'
              : 'rounded-lg w-full max-w-3xl mx-4'
          )}
        >
          {/* Header */}
          <div
            className={cn(
              'flex items-center justify-between border-b border-line',
              responsive ? 'px-4 md:px-6 py-4' : 'px-6 py-4'
            )}
          >
            {responsive && (
              <div className="absolute top-2 left-1/2 -translate-x-1/2 w-10 h-1 bg-surface-emphasis rounded-full md:hidden" />
            )}
            <div className={cn(responsive && 'mt-2 md:mt-0')}>
              <h2 className="text-lg font-semibold flex items-center gap-2">
                <Eye size={20} className="text-primary-400" />
                Checkpoint Diff
              </h2>
              <p className="text-xs text-fg-subtle">
                {checkpoint ? (
                  <>
                    Comparing <code className="bg-surface-muted px-1 rounded">{checkpoint.commitHash.slice(0, 7)}</code> → Current
                  </>
                ) : (
                  'Loading...'
                )}
              </p>
            </div>
            <div className="flex items-center gap-2">
              <Button
                variant="ghost"
                size="icon"
                onClick={handleRefresh}
                disabled={refreshing}
                title="Refresh"
                className={cn(responsive && 'min-w-[44px] min-h-[44px]')}
              >
                <RefreshCw size={18} className={cn(refreshing && 'animate-spin')} />
              </Button>
              <Button
                variant="ghost"
                size="icon"
                onClick={onClose}
                className={cn(responsive && 'min-w-[44px] min-h-[44px]')}
              >
                <X size={20} />
              </Button>
            </div>
          </div>

          {/* Summary */}
          {diff && (
            <div className="px-4 py-3 bg-surface-base/50 border-b border-line">
              <div className="flex items-center justify-between text-sm">
                <div className="flex items-center gap-4">
                  <span className="text-green-400">+{diff.totalInsertions}</span>
                  <span className="text-red-400">-{diff.totalDeletions}</span>
                  <span className="text-fg-muted">across {diff.files.length} files</span>
                </div>
                <button
                  onClick={toggleSelectAll}
                  className="text-xs text-primary-400 hover:text-primary-300"
                >
                  {selectedFiles.size === diff.files.length ? 'Deselect All' : 'Select All'}
                </button>
              </div>
            </div>
          )}

          {/* File List */}
          <div className="flex-1 overflow-y-auto">
            {loading ? (
              <LoadingSkeleton />
            ) : !diff || diff.files.length === 0 ? (
              <div className="flex flex-col items-center justify-center py-12 text-fg-subtle">
                <Eye size={48} className="mb-4 opacity-50" />
                <p className="text-center">No changes detected</p>
                <p className="text-xs text-fg-subtle mt-2 text-center">
                  The workspace matches this checkpoint
                </p>
              </div>
            ) : (
              <motion.div
                initial={{ opacity: 0 }}
                animate={{ opacity: 1 }}
                className="divide-y divide-line/50"
              >
                {diff.files.map((file) => {
                  const isSelected = selectedFiles.has(file.path);
                  const isExpanded = expandedFile === file.path;

                  return (
                    <div key={file.path}>
                      {/* File Header */}
                      <div
                        className={cn(
                          'flex items-center gap-3 px-4 py-2',
                          'hover:bg-surface-base/50 transition-colors cursor-pointer'
                        )}
                      >
                        {/* Checkbox */}
                        <button
                          onClick={() => toggleFileSelection(file.path)}
                          className={cn(
                            'w-4 h-4 rounded border transition-colors flex items-center justify-center',
                            isSelected
                              ? 'bg-primary-500 border-primary-500'
                              : 'border-line-muted hover:border-fg-subtle'
                          )}
                        >
                          {isSelected && <Check size={12} className="text-white" />}
                        </button>

                        {/* Expand Icon */}
                        <button
                          onClick={() => handleViewFileDiff(file.path)}
                          className="text-fg-subtle hover:text-fg-secondary"
                        >
                          {isExpanded ? <ChevronDown size={14} /> : <ChevronRight size={14} />}
                        </button>

                        {/* Change Type */}
                        <span
                          className={cn(
                            'w-5 h-5 rounded text-xs font-bold flex items-center justify-center',
                            getChangeColor(file.type)
                          )}
                        >
                          {getChangeLabel(file.type)}
                        </span>

                        {/* File Path */}
                        <span
                          className="flex-1 text-sm font-mono truncate text-fg-secondary"
                          onClick={() => handleViewFileDiff(file.path)}
                        >
                          {file.path}
                        </span>

                        {/* Stats */}
                        {(file.insertions !== undefined || file.deletions !== undefined) && (
                          <span className="text-xs text-fg-subtle">
                            {file.insertions !== undefined && (
                              <span className="text-green-400 mr-2">+{file.insertions}</span>
                            )}
                            {file.deletions !== undefined && (
                              <span className="text-red-400">-{file.deletions}</span>
                            )}
                          </span>
                        )}
                      </div>

                      {/* File Diff Content */}
                      <AnimatePresence>
                        {isExpanded && (
                          <motion.div
                            initial={{ height: 0, opacity: 0 }}
                            animate={{ height: 'auto', opacity: 1 }}
                            exit={{ height: 0, opacity: 0 }}
                            transition={{ duration: 0.2 }}
                            className="overflow-hidden bg-surface-base/30"
                          >
                            {loadingFileDiff ? (
                              <div className="p-4">
                                <Skeleton className="h-32 w-full" />
                              </div>
                            ) : fileDiff?.patch ? (
                              <div className="max-h-64 overflow-auto border-t border-line/50">
                                {renderPatch(fileDiff.patch)}
                              </div>
                            ) : (
                              <div className="p-4 text-center text-fg-subtle text-sm">
                                No diff content available
                              </div>
                            )}
                          </motion.div>
                        )}
                      </AnimatePresence>
                    </div>
                  );
                })}
              </motion.div>
            )}
          </div>

          {/* Footer Actions */}
          {diff && diff.files.length > 0 && (onRestoreSelected || onRestoreAll) && (
            <div
              className={cn(
                'flex items-center justify-between border-t border-line',
                responsive ? 'px-4 py-3 safe-area-pb' : 'px-6 py-3'
              )}
            >
              <span className="text-xs text-fg-subtle">
                {selectedFiles.size} of {diff.files.length} files selected
              </span>
              <div className="flex items-center gap-2">
                {onRestoreSelected && (
                  <Button
                    variant="outline"
                    size="sm"
                    onClick={handleRestoreSelected}
                    disabled={selectedFiles.size === 0}
                    className="text-green-400 border-green-400/50 hover:border-green-400"
                  >
                    <RotateCcw size={14} className="mr-1" />
                    Restore Selected ({selectedFiles.size})
                  </Button>
                )}
                {onRestoreAll && (
                  <Button
                    variant="default"
                    size="sm"
                    onClick={handleRestoreAll}
                  >
                    <RotateCcw size={14} className="mr-1" />
                    Restore All
                  </Button>
                )}
              </div>
            </div>
          )}
        </motion.div>
      </motion.div>
    </AnimatePresence>
  );
}
