"use client";

import { useState, useCallback, useEffect } from "react";

interface FileEntry {
  path: string;
  name: string;
  content: string;
  language: string;
}

interface TreeNode {
  name: string;
  path: string;
  isDirectory: boolean;
  children?: TreeNode[];
  file?: FileEntry;
}

interface FileTreeProps {
  repositoryId: string | null;
  onSelectFile: (file: FileEntry) => void;
}

interface ApiFileEntry {
  name: string;
  path: string;
  type: "file" | "directory";
}

export function FileTree({ repositoryId, onSelectFile }: FileTreeProps) {
  const [nodes, setNodes] = useState<TreeNode[]>([]);
  const [expandedDirs, setExpandedDirs] = useState<Set<string>>(new Set());
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const loadDirectory = useCallback(async (dirPath: string): Promise<TreeNode[]> => {
    if (!repositoryId) return [];
    const res = await fetch("/api/files", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ repository_id: repositoryId, path: dirPath }),
    });
    if (!res.ok) {
      const data = await res.json().catch(() => ({}));
      throw new Error(data?.error || "Failed to load files");
    }
    const data = await res.json();
    return (data.entries as ApiFileEntry[]).map((entry) => ({
      name: entry.name,
      path: entry.path,
      isDirectory: entry.type === "directory",
    }));
  }, [repositoryId]);

  const replaceChildren = useCallback((list: TreeNode[], targetPath: string, children: TreeNode[]): TreeNode[] => {
    return list.map((node) => {
      if (node.path === targetPath) {
        return { ...node, children };
      }
      if (node.children) {
        return { ...node, children: replaceChildren(node.children, targetPath, children) };
      }
      return node;
    });
  }, []);

  useEffect(() => {
    if (!repositoryId) {
      setNodes([]);
      setExpandedDirs(new Set());
      return;
    }
    let cancelled = false;
    setLoading(true);
    setError(null);
    loadDirectory(".")
      .then((rootNodes) => {
        if (!cancelled) {
          setNodes(rootNodes);
          setExpandedDirs(new Set());
        }
      })
      .catch((err) => {
        if (!cancelled) {
          setError(err instanceof Error ? err.message : "Failed to load files");
          setNodes([]);
        }
      })
      .finally(() => {
        if (!cancelled) setLoading(false);
      });
    return () => {
      cancelled = true;
    };
  }, [repositoryId, loadDirectory]);

  const toggleDir = useCallback((path: string) => {
    setExpandedDirs((prev) => {
      const next = new Set(prev);
      if (next.has(path)) {
        next.delete(path);
      } else {
        next.add(path);
      }
      return next;
    });
  }, []);

  const handleToggleDir = useCallback(async (path: string, node: TreeNode) => {
    const isExpanded = expandedDirs.has(path);
    toggleDir(path);
    if (!isExpanded && node.children === undefined) {
      try {
        const children = await loadDirectory(path);
        setNodes((prev) => replaceChildren(prev, path, children));
      } catch (err) {
        setError(err instanceof Error ? err.message : "Failed to load folder");
      }
    }
  }, [expandedDirs, toggleDir, loadDirectory, replaceChildren]);

  const handleSelectFile = useCallback(async (path: string) => {
    if (!repositoryId) return;
    try {
      const res = await fetch("/api/files/read", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ repository_id: repositoryId, path }),
      });
      if (!res.ok) {
        const data = await res.json().catch(() => ({}));
        throw new Error(data?.error || "Failed to read file");
      }
      const data = await res.json();
      onSelectFile({
        path: data.path,
        name: data.path.split("/").pop() || data.path,
        content: data.content,
        language: data.language,
      });
    } catch (err) {
      setError(err instanceof Error ? err.message : "Failed to read file");
    }
  }, [repositoryId, onSelectFile]);

  if (!repositoryId) {
    return (
      <div className="py-1">
        <div className="px-3 py-2 text-r-xs text-rp-faint text-center">
          Select a repository first
        </div>
      </div>
    );
  }

  if (loading) {
    return (
      <div className="py-1">
        <div className="px-3 py-2 text-r-xs text-rp-faint text-center">
          Loading files...
        </div>
      </div>
    );
  }

  if (error) {
    return (
      <div className="py-1">
        <div className="px-3 py-2 text-r-xs text-rp-error text-center">
          {error}
        </div>
      </div>
    );
  }

  if (nodes.length === 0) {
    return (
      <div className="py-1">
        <div className="px-3 py-2 text-r-xs text-rp-faint text-center">
          No files loaded
        </div>
      </div>
    );
  }

  return (
    <div className="py-1">
      {nodes.map((node) => (
        <TreeNodeItem
          key={node.path}
          node={node}
          depth={0}
          expandedDirs={expandedDirs}
          onToggleDir={handleToggleDir}
          onSelectFile={handleSelectFile}
        />
      ))}
    </div>
  );
}

export function TreeNodeItem({
  node,
  depth,
  expandedDirs,
  onToggleDir,
  onSelectFile,
}: {
  node: TreeNode;
  depth: number;
  expandedDirs: Set<string>;
  onToggleDir: (path: string, node: TreeNode) => void;
  onSelectFile: (path: string) => void;
}) {
  const isExpanded = expandedDirs.has(node.path);
  const paddingLeft = 12 + depth * 12;

  if (node.isDirectory) {
    return (
      <div>
        <button
          onClick={() => onToggleDir(node.path, node)}
          className="w-full flex items-center gap-1.5 py-1 text-r-sm text-rp-muted hover:text-rp-text hover:bg-rp-hover/30 transition-colors"
          style={{ paddingLeft }}
        >
          <svg
            width="12"
            height="12"
            viewBox="0 0 12 12"
            fill="none"
            stroke="currentColor"
            strokeWidth="1.5"
            className={`shrink-0 transition-transform ${isExpanded ? "rotate-90" : ""}`}
          >
            <path d="M4 2L8 6L4 10" />
          </svg>
          <span className="truncate">{node.name}</span>
        </button>
        {isExpanded && node.children && (
          <div>
            {node.children.map((child) => (
              <TreeNodeItem
                key={child.path}
                node={child}
                depth={depth + 1}
                expandedDirs={expandedDirs}
                onToggleDir={onToggleDir}
                onSelectFile={onSelectFile}
              />
            ))}
          </div>
        )}
      </div>
    );
  }

  return (
    <button
      onClick={() => onSelectFile(node.path)}
      className="w-full flex items-center gap-1.5 py-1 text-r-sm text-rp-muted hover:text-rp-text hover:bg-rp-hover/30 transition-colors"
      style={{ paddingLeft: paddingLeft + 14 }}
    >
      <span className="truncate">{node.name}</span>
    </button>
  );
}

