/**
 * CodeSnippetPanel - 代码片段快速浏览面板
 *
 * 功能：
 * - 搜索过滤代码片段
 * - 按语言筛选
 * - 展开/折叠查看完整代码
 * - 一键复制
 * - 下载为文件
 */

import { useState, useMemo, useCallback } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import {
  Search, Copy, Download, ChevronDown, ChevronUp,
  FileCode, X, Check, Code
} from 'lucide-react';
import { Button, EmptyState } from './ui';
import type { CodeBlock } from '../store/chatStore';

// === Types ===

export interface CodeSnippet {
  id: string;
  block: CodeBlock;
  messageIndex: number;
}

interface CodeSnippetPanelProps {
  snippets: CodeSnippet[];
}

// === Language Colors ===

const LANGUAGE_COLORS: Record<string, string> = {
  python: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300',
  javascript: 'bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-300',
  typescript: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300',
  rust: 'bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-300',
  go: 'bg-cyan-100 text-cyan-700 dark:bg-cyan-900/30 dark:text-cyan-300',
  java: 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-300',
  cpp: 'bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-300',
  c: 'bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300',
  html: 'bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-300',
  css: 'bg-pink-100 text-pink-700 dark:bg-pink-900/30 dark:text-pink-300',
  sql: 'bg-indigo-100 text-indigo-700 dark:bg-indigo-900/30 dark:text-indigo-300',
  bash: 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300',
  shell: 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300',
  json: 'bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300',
  yaml: 'bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300',
  markdown: 'bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300',
  text: 'bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-400',
};

function getLanguageColor(lang: string): string {
  return LANGUAGE_COLORS[lang.toLowerCase()] || LANGUAGE_COLORS.text;
}

function getFileExtension(lang?: string): string {
  const extensions: Record<string, string> = {
    python: 'py',
    javascript: 'js',
    typescript: 'ts',
    rust: 'rs',
    go: 'go',
    java: 'java',
    cpp: 'cpp',
    c: 'c',
    html: 'html',
    css: 'css',
    sql: 'sql',
    bash: 'sh',
    shell: 'sh',
    json: 'json',
    yaml: 'yaml',
    markdown: 'md',
  };
  return extensions[lang?.toLowerCase() || ''] || 'txt';
}

// === Snippet Card Component ===

interface SnippetCardProps {
  snippet: CodeSnippet;
  isExpanded: boolean;
  onToggle: () => void;
}

function SnippetCard({ snippet, isExpanded, onToggle }: SnippetCardProps) {
  const [copied, setCopied] = useState(false);
  const { block, messageIndex } = snippet;

  const handleCopy = useCallback(async (e: React.MouseEvent) => {
    e.stopPropagation();
    try {
      await navigator.clipboard.writeText(block.content || '');
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    } catch (err) {
      console.error('Failed to copy:', err);
    }
  }, [block.content]);

  const handleDownload = useCallback((e: React.MouseEvent) => {
    e.stopPropagation();
    const filename = block.filename || `snippet_${messageIndex + 1}.${getFileExtension(block.language)}`;
    const blob = new Blob([block.content || ''], { type: 'text/plain' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = filename;
    a.click();
    URL.revokeObjectURL(url);
  }, [block.content, block.filename, block.language, messageIndex]);

  const lineCount = (block.content || '').split('\n').length;
  const charCount = (block.content || '').length;
  const previewLines = (block.content || '').split('\n').slice(0, 2).join('\n');

  return (
    <div className="border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden">
      {/* Header */}
      <button
        onClick={onToggle}
        className="w-full flex items-center justify-between p-3 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors text-left"
      >
        <div className="flex items-center gap-3 min-w-0">
          <FileCode className="w-4 h-4 text-gray-400 flex-shrink-0" />
          <div className="min-w-0">
            <div className="flex items-center gap-2">
              <span className="font-medium text-gray-800 dark:text-gray-200 truncate text-sm">
                {block.filename || `代码片段 #${messageIndex + 1}`}
              </span>
              <span className={`text-xs px-1.5 py-0.5 rounded font-medium ${getLanguageColor(block.language || 'text')}`}>
                {block.language || 'text'}
              </span>
            </div>
            <div className="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
              {lineCount} 行 · {charCount > 1024 ? `${(charCount / 1024).toFixed(1)} KB` : `${charCount} 字符`}
            </div>
          </div>
        </div>
        <div className="flex items-center gap-1">
          <Button
            variant="ghost"
            size="sm"
            onClick={handleCopy}
            className="p-1.5 opacity-60 hover:opacity-100"
            title="复制代码"
          >
            {copied ? (
              <Check className="w-4 h-4 text-green-500" />
            ) : (
              <Copy className="w-4 h-4" />
            )}
          </Button>
          <Button
            variant="ghost"
            size="sm"
            onClick={handleDownload}
            className="p-1.5 opacity-60 hover:opacity-100"
            title="下载文件"
          >
            <Download className="w-4 h-4" />
          </Button>
          {isExpanded ? (
            <ChevronUp className="w-4 h-4 text-gray-400 ml-1" />
          ) : (
            <ChevronDown className="w-4 h-4 text-gray-400 ml-1" />
          )}
        </div>
      </button>

      {/* Preview / Full Content */}
      <AnimatePresence initial={false}>
        {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"
          >
            <div className="border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900 p-3">
              <pre className="text-xs font-mono text-gray-700 dark:text-gray-300 overflow-x-auto whitespace-pre-wrap break-all max-h-60 overflow-y-auto">
                {block.content}
              </pre>
            </div>
          </motion.div>
        ) : (
          <div className="px-3 pb-3">
            <pre className="text-xs font-mono text-gray-500 dark:text-gray-400 overflow-hidden whitespace-pre-wrap break-all line-clamp-2">
              {previewLines}
              {(block.content || '').split('\n').length > 2 && '...'}
            </pre>
          </div>
        )}
      </AnimatePresence>
    </div>
  );
}

// === Main Component ===

export function CodeSnippetPanel({ snippets }: CodeSnippetPanelProps) {
  const [searchQuery, setSearchQuery] = useState('');
  const [selectedLanguage, setSelectedLanguage] = useState<string | null>(null);
  const [expandedId, setExpandedId] = useState<string | null>(null);

  // Get unique languages with counts
  const languages = useMemo(() => {
    const langMap = new Map<string, number>();
    snippets.forEach(s => {
      const lang = s.block.language?.toLowerCase() || 'text';
      langMap.set(lang, (langMap.get(lang) || 0) + 1);
    });
    return Array.from(langMap.entries())
      .map(([lang, count]) => ({ lang, count }))
      .sort((a, b) => b.count - a.count);
  }, [snippets]);

  // Filter snippets
  const filteredSnippets = useMemo(() => {
    return snippets.filter(snippet => {
      const block = snippet.block;

      // Language filter
      if (selectedLanguage && (block.language?.toLowerCase() || 'text') !== selectedLanguage) {
        return false;
      }

      // Search filter
      if (searchQuery) {
        const query = searchQuery.toLowerCase();
        const matchesFilename = block.filename?.toLowerCase().includes(query);
        const matchesContent = block.content?.toLowerCase().includes(query);
        const matchesLanguage = block.language?.toLowerCase().includes(query);
        return matchesFilename || matchesContent || matchesLanguage;
      }

      return true;
    });
  }, [snippets, searchQuery, selectedLanguage]);

  const handleToggle = useCallback((id: string) => {
    setExpandedId(prev => prev === id ? null : id);
  }, []);

  if (snippets.length === 0) {
    return (
      <EmptyState
        icon={<Code className="w-10 h-10" />}
        title="暂无代码片段"
        description="对话中生成的代码会自动出现在这里"
        className="py-8"
      />
    );
  }

  return (
    <div className="space-y-3">
      {/* Search Bar */}
      <div className="relative">
        <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400" />
        <input
          type="text"
          placeholder="搜索代码..."
          value={searchQuery}
          onChange={(e) => setSearchQuery(e.target.value)}
          className="w-full pl-9 pr-8 py-2 bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:border-transparent"
        />
        {searchQuery && (
          <button
            onClick={() => setSearchQuery('')}
            className="absolute right-2 top-1/2 -translate-y-1/2 p-1 hover:bg-gray-200 dark:hover:bg-gray-700 rounded"
          >
            <X className="w-3 h-3 text-gray-400" />
          </button>
        )}
      </div>

      {/* Language Filters */}
      {languages.length > 1 && (
        <div className="flex flex-wrap gap-1.5">
          <button
            onClick={() => setSelectedLanguage(null)}
            className={`px-2 py-1 text-xs rounded-full transition-colors ${
              selectedLanguage === null
                ? 'bg-emerald-100 dark:bg-emerald-900/30 text-emerald-700 dark:text-emerald-300'
                : 'bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700'
            }`}
          >
            全部 ({snippets.length})
          </button>
          {languages.map(({ lang, count }) => (
            <button
              key={lang}
              onClick={() => setSelectedLanguage(selectedLanguage === lang ? null : lang)}
              className={`px-2 py-1 text-xs rounded-full transition-colors ${
                selectedLanguage === lang
                  ? 'bg-emerald-100 dark:bg-emerald-900/30 text-emerald-700 dark:text-emerald-300'
                  : 'bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700'
              }`}
            >
              {lang} ({count})
            </button>
          ))}
        </div>
      )}

      {/* Results Count */}
      {(searchQuery || selectedLanguage) && (
        <div className="text-xs text-gray-500 dark:text-gray-400">
          找到 {filteredSnippets.length} 个代码片段
        </div>
      )}

      {/* Snippet List */}
      <div className="space-y-2 overflow-y-auto" style={{ maxHeight: 'calc(100vh - 380px)' }}>
        <AnimatePresence mode="popLayout">
          {filteredSnippets.map((snippet) => (
            <motion.div
              key={snippet.id}
              initial={{ opacity: 0, y: 10 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: -10 }}
              layout
            >
              <SnippetCard
                snippet={snippet}
                isExpanded={expandedId === snippet.id}
                onToggle={() => handleToggle(snippet.id)}
              />
            </motion.div>
          ))}
        </AnimatePresence>

        {filteredSnippets.length === 0 && (
          <div className="text-center py-4 text-sm text-gray-500 dark:text-gray-400">
            没有找到匹配的代码片段
          </div>
        )}
      </div>
    </div>
  );
}
