import React, { useState, useEffect } from 'react';
import { ScrollArea } from "@/components/ui/scroll-area";
import { Badge } from "@/components/ui/badge";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { ChevronRight, ChevronDown, FileText, Folder, FileJson, RefreshCw, GitBranch, GitCommit } from 'lucide-react';
import { cn } from "@/lib/utils";

// Types mirroring the Rust backend
type EntryKind = 'File' | 'Dir';

interface DirEntry {
  name: string;
  kind: EntryKind;
  object_id: string;
}

interface ContextExplorerProps {
  reqId: string;
  commitHash: string;
  diffTargetHash?: string; // If present, show diff against this commit
  className?: string;
  highlightPaths?: string[]; // Paths to auto-expand
}

type DiffStatus = 'added' | 'modified' | 'deleted' | 'none';

interface TreeNodeProps {
  reqId: string;
  commitHash: string;
  path: string;
  name: string;
  kind: EntryKind;
  depth: number;
  getDiffStatus: (path: string) => DiffStatus;
  onPreview: (path: string, kind: EntryKind) => void;
}

const TreeNode: React.FC<TreeNodeProps> = ({ 
  reqId, commitHash, path, name, kind, depth, getDiffStatus, onPreview 
}) => {
  const [expanded, setExpanded] = useState(false);
  const [children, setChildren] = useState<DirEntry[] | null>(null);
  const [loading, setLoading] = useState(false);

  const diffStatus = getDiffStatus(path);

  const toggleExpand = async (e: React.MouseEvent) => {
    e.stopPropagation();
    if (kind === 'File') {
      onPreview(path, kind);
      return;
    }

    const nextExpanded = !expanded;
    setExpanded(nextExpanded);

    if (nextExpanded && !children) {
      setLoading(true);
      try {
        // The path logic: root is empty string. Subdirs are "dir/subdir".
        const queryPath = path;
        const res = await fetch(`/api/context/${reqId}/tree/${commitHash}?path=${encodeURIComponent(queryPath)}`);
        if (res.ok) {
          const data = await res.json();
          // Sort: Dirs first, then Files
          data.sort((a: DirEntry, b: DirEntry) => {
            if (a.kind === b.kind) return a.name.localeCompare(b.name);
            return a.kind === 'Dir' ? -1 : 1;
          });
          setChildren(data);
        }
      } catch (err) {
        console.error("Failed to load tree", err);
      } finally {
        setLoading(false);
      }
    }
  };

  // Icons
  const Icon = kind === 'Dir' ? Folder : (name.endsWith('.json') ? FileJson : FileText);
  
  const getDiffColor = () => {
    switch (diffStatus) {
      case 'added': return 'text-green-600 font-medium dark:text-green-400';
      case 'modified': return 'text-yellow-600 font-medium dark:text-yellow-400';
      case 'deleted': return 'text-red-600 line-through dark:text-red-400';
      default: return 'text-foreground';
    }
  };

  return (
    <div className="select-none">
      <div 
        className={cn(
          "flex items-center py-1 px-2 hover:bg-accent/50 rounded-sm cursor-pointer transition-colors",
          getDiffColor()
        )}
        style={{ paddingLeft: `${depth * 12 + 4}px` }}
        onClick={toggleExpand}
      >
        <div className="w-4 h-4 mr-1 flex items-center justify-center opacity-70">
          {kind === 'Dir' && (
            loading ? <RefreshCw className="w-3 h-3 animate-spin" /> :
            expanded ? <ChevronDown className="w-3 h-3" /> : <ChevronRight className="w-3 h-3" />
          )}
        </div>
        <Icon className={cn("w-4 h-4 mr-2", kind === 'Dir' ? "text-blue-500" : "text-gray-500")} />
        <span className="text-sm truncate">{name}</span>
        {diffStatus && diffStatus !== 'none' && (
          <Badge variant="outline" className={cn("ml-2 text-[10px] h-4 px-1", 
            diffStatus === 'added' ? "border-green-500 text-green-600" : 
            diffStatus === 'modified' ? "border-yellow-500 text-yellow-600" : "")}>
            {diffStatus}
          </Badge>
        )}
      </div>
      
      {expanded && children && (
        <div>
          {children.map((child) => (
            <TreeNode
              key={child.object_id + child.name}
              reqId={reqId}
              commitHash={commitHash}
              path={path ? `${path}/${child.name}` : child.name}
              name={child.name}
              kind={child.kind}
              depth={depth + 1}
              getDiffStatus={getDiffStatus}
              onPreview={onPreview}
            />
          ))}
        </div>
      )}
    </div>
  );
};

export const ContextExplorer: React.FC<ContextExplorerProps> = ({ 
  reqId, commitHash, diffTargetHash, className 
}) => {
  const [rootEntries, setRootEntries] = useState<DirEntry[]>([]);
  const [previewContent, setPreviewContent] = useState<string | null>(null);
  const [previewPath, setPreviewPath] = useState<string | null>(null);
  const [diffMap, setDiffMap] = useState<Map<string, DiffStatus>>(new Map());
  
  useEffect(() => {
    if (!reqId || !commitHash) return;
    
    const fetchRoot = async () => {
      try {
        const res = await fetch(`/api/context/${reqId}/tree/${commitHash}?path=`);
        if (res.ok) {
          const data = await res.json();
          if (Array.isArray(data)) {
              data.sort((a: DirEntry, b: DirEntry) => {
                if (a.kind === b.kind) return a.name.localeCompare(b.name);
                return a.kind === 'Dir' ? -1 : 1;
              });
              setRootEntries(data);
          } else {
              console.error("ContextExplorer: Expected array from tree API, got:", data);
              setRootEntries([]);
          }
        } else {
            console.error("ContextExplorer: Fetch failed", res.status, res.statusText);
        }
      } catch (e) {
        console.error(e);
      }
    };

    const fetchDiff = async () => {
        if (!diffTargetHash || diffTargetHash === commitHash) {
            setDiffMap(new Map());
            return;
        }
        try {
            const res = await fetch(`/api/context/${reqId}/diff/${diffTargetHash}/${commitHash}`);
            if (res.ok) {
                const changes = await res.json();
                const map = new Map<string, DiffStatus>();
                // Rust enum serialization: { "Added": "path" }
                changes.forEach((change: any) => {
                    if (change.Added) map.set(change.Added, 'added');
                    else if (change.Modified) map.set(change.Modified, 'modified');
                    else if (change.Deleted) map.set(change.Deleted, 'deleted');
                });
                setDiffMap(map);
            }
        } catch (e) {
            console.error("Failed to fetch diff", e);
        }
    };
    
    fetchRoot();
    fetchDiff();
    setPreviewContent(null);
    setPreviewPath(null);
  }, [reqId, commitHash, diffTargetHash]);

  const getDiffStatus = (path: string) => diffMap.get(path) || 'none';

  const handlePreview = async (path: string, kind: EntryKind) => {
    if (kind !== 'File') return;
    
    setPreviewPath(path);
    setPreviewContent("Loading...");
    
    try {
      const res = await fetch(`/api/context/${reqId}/blob/${commitHash}/${encodeURIComponent(path)}`);
      if (res.ok) {
        const text = await res.text();
        // Try to format JSON
        try {
            if (path.endsWith('.json')) {
                const obj = JSON.parse(text);
                setPreviewContent(JSON.stringify(obj, null, 2));
            } else {
                setPreviewContent(text);
            }
        } catch {
            setPreviewContent(text);
        }
      } else {
        setPreviewContent(`Error loading content: ${res.statusText}`);
      }
    } catch (e) {
      setPreviewContent(`Error: ${e}`);
    }
  };

  if (!reqId || !commitHash) {
    return (
      <Card className={cn("h-full flex items-center justify-center text-muted-foreground", className)}>
         Select a task to view context
      </Card>
    );
  }

  return (
    <div className={cn("grid grid-cols-3 gap-4 h-full max-h-full", className)}>
      {/* Tree View */}
      <Card className="col-span-1 flex flex-col overflow-hidden min-h-0 h-full">
        <CardHeader className="py-3 px-4 bg-muted/30 border-b shrink-0">
          <CardTitle className="text-sm font-medium flex items-center gap-2">
            <GitCommit className="w-4 h-4" />
            Context: {commitHash.substring(0, 7)}
            {diffTargetHash && diffTargetHash !== commitHash && (
                <Badge variant="secondary" className="ml-auto text-[10px] h-5">Diff Mode</Badge>
            )}
          </CardTitle>
        </CardHeader>
        <ScrollArea className="flex-1 p-2 h-full">
          {rootEntries.map(entry => (
            <TreeNode
              key={entry.object_id}
              reqId={reqId}
              commitHash={commitHash}
              path={entry.name}
              name={entry.name}
              kind={entry.kind}
              depth={0}
              getDiffStatus={getDiffStatus}
              onPreview={handlePreview}
            />
          ))}
        </ScrollArea>
      </Card>

      {/* Preview Pane */}
      <Card className="col-span-2 flex flex-col overflow-hidden min-h-0 h-full">
        <CardHeader className="py-3 px-4 bg-muted/30 border-b shrink-0">
          <CardTitle className="text-sm font-medium flex items-center gap-2">
            <FileText className="w-4 h-4" />
            {previewPath || "Preview"}
          </CardTitle>
        </CardHeader>
        <ScrollArea className="flex-1 h-full bg-background w-full rounded-b-lg">
          <div className="p-4">
            {previewContent ? (
              <pre className="text-xs font-mono whitespace-pre-wrap break-words text-foreground/90">
                {previewContent}
              </pre>
            ) : (
              <div className="flex items-center justify-center h-full min-h-[200px] text-muted-foreground text-sm">
                Select a file to view content
              </div>
            )}
          </div>
        </ScrollArea>
      </Card>
    </div>
  );
};

