import React, { useCallback, useMemo, useState } from "react";
import { useQuery } from "@tanstack/react-query";
import { CornerUpLeft, Download, FileIcon, FileText, FolderIcon, HardDriveDownload, Loader2, MessageSquare, X } from "lucide-react";
import { gitBlobContent, gitCommitLog, gitTreeList } from "@/client";
import { useRepo } from "@/contexts";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { MiniChat } from "@/components/shared/MiniChat";

type TreeEntry = {
  name: string;
  oid: string;
  kind: string;
  filemode: number;
  is_binary: boolean;
};

interface FileBrowserProps {
  branch?: string;
  initialPath?: string;
}

export const FileBrowser = ({ branch, initialPath = "" }: FileBrowserProps) => {
  const repo = useRepo();
  const [currentPath, setCurrentPath] = useState(initialPath);
  const [selectedFile, setSelectedFile] = useState<string | null>(null);
  const [previewFile, setPreviewFile] = useState<{ path: string; name: string; oid: string } | null>(null);
  const [previewTab, setPreviewTab] = useState<'content' | 'chat'>('content');

  // Reset to content tab when a new file is opened
  const handlePreviewFile = useCallback((file: { path: string; name: string; oid: string } | null) => {
    setPreviewFile(file);
    setPreviewTab('content');
  }, []);

  // Get the latest commit to find the tree_id for the current branch
  const { data: commitsData, isLoading: commitsLoading } = useQuery({
    queryKey: ["repo-commits-for-tree", repo?.namespace, repo?.repo_name, branch],
    queryFn: async () => {
      if (!repo?.namespace || !repo?.repo_name) return null;
      const resp = await gitCommitLog({ path: { namespace: repo.namespace, repo: repo.repo_name } });
      return resp.data?.data ?? [];
    },
    enabled: !!repo?.namespace && !!repo?.repo_name,
    staleTime: 30 * 1000,
  });

  const latestCommit = commitsData && "data" in commitsData ? commitsData.data[0] : null;
  const rootTreeId = latestCommit?.tree_id;

  // Fetch tree entries for current path (using tree OID)
  const { data: treeEntries, isLoading: treeLoading, isError, refetch } = useQuery({
    queryKey: ["tree-entries", repo?.namespace, repo?.repo_name, rootTreeId, currentPath],
    queryFn: async (): Promise<TreeEntry[]> => {
      if (!repo?.namespace || !repo?.repo_name || !rootTreeId) return [];

      // If we're at root, use the root tree ID
      // If we're in a subdirectory, we need to find the tree OID for that path
      // This requires getting the tree entries recursively
      const entries = await getTreeEntries(repo.namespace, repo.repo_name, rootTreeId, currentPath);
      return entries;
    },
    enabled: !!repo?.namespace && !!repo?.repo_name && !!rootTreeId,
  });

  const sortedEntries = useMemo(() => {
    if (!treeEntries) return [];
    return [...treeEntries].sort((a, b) => {
      // Directories first
      if (a.kind === "tree" && b.kind !== "tree") return -1;
      if (b.kind === "tree" && a.kind !== "tree") return 1;
      return a.name.localeCompare(b.name);
    });
  }, [treeEntries]);

  const updatePath = useCallback(
    (path: string) => {
      setCurrentPath(path);
      setSelectedFile(null);
    },
    []
  );

  const handleFileClick = useCallback(
    (entry: TreeEntry) => {
      if (entry.kind === "tree") {
        // Navigate into directory
        updatePath(currentPath ? `${currentPath}/${entry.name}` : entry.name);
      } else {
        // Open file preview for markdown or show download for others
        if (/\.(md|mdx)$/i.test(entry.name)) {
          handlePreviewFile({ path: currentPath ? `${currentPath}/${entry.name}` : entry.name, name: entry.name, oid: entry.oid });
        } else {
          setSelectedFile(entry.name);
        }
      }
    },
    [currentPath, updatePath, handlePreviewFile]
  );

  const handleGoBack = useCallback(() => {
    if (!currentPath) return;
    const segments = currentPath.split("/");
    segments.pop();
    updatePath(segments.join("/"));
  }, [currentPath, updatePath]);

  // Fetch blob content for preview
  const { data: blobContent, isLoading: blobLoading } = useQuery({
    queryKey: ["blob-content", repo?.namespace, repo?.repo_name, previewFile?.oid],
    queryFn: async () => {
      if (!repo?.namespace || !repo?.repo_name || !previewFile?.oid) return null;
      const resp = await gitBlobContent({
        path: { namespace: repo.namespace, repo: repo.repo_name, oid: previewFile.oid },
      });
      const data = resp.data?.data;
      if (!data) return null;
      // Content is base64 encoded
      try {
        return atob(data.content);
      } catch {
        return data.content;
      }
    },
    enabled: !!repo?.namespace && !!repo?.repo_name && !!previewFile?.oid,
  });

  const handleCopy = () => {
    if (blobContent) {
      navigator.clipboard.writeText(blobContent);
    }
  };

  const handleDownload = () => {
    if (!blobContent || !previewFile) return;
    const blob = new Blob([blobContent], { type: "text/plain" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = previewFile.name;
    a.click();
    URL.revokeObjectURL(url);
  };

  if (!repo) {
    return <div className="p-4 text-muted-foreground">No repository selected</div>;
  }

  if (commitsLoading) {
    return (
      <div className="flex items-center justify-center h-64">
        <Loader2 className="h-5 w-5 animate-spin mr-2" />
        <span className="text-muted-foreground">Loading repository...</span>
      </div>
    );
  }

  if (!latestCommit) {
    return (
      <div className="border rounded-md">
        <div className="p-8 text-center text-muted-foreground">
          <HardDriveDownload className="h-10 w-10 mx-auto mb-2 opacity-50" />
          <p>This repository is empty</p>
          <p className="text-sm mt-1">No commits yet, so no files to display</p>
        </div>
      </div>
    );
  }

  if (treeLoading) {
    return (
      <div className="flex items-center justify-center h-64">
        <Loader2 className="h-5 w-5 animate-spin mr-2" />
        <span className="text-muted-foreground">Loading files...</span>
      </div>
    );
  }

  if (isError) {
    return (
      <div className="p-4">
        <p className="text-sm text-destructive">Failed to load files</p>
        <button
          onClick={() => refetch()}
          className="mt-2 px-3 py-1.5 text-sm border rounded-md hover:bg-muted"
        >
          Retry
        </button>
      </div>
    );
  }

  return (
    <div className="space-y-2">
      {currentPath && (
        <div
          className="flex items-center gap-3 px-4 py-2 border border-border rounded-md cursor-pointer hover:bg-muted transition-colors"
          onClick={handleGoBack}
        >
          <CornerUpLeft className="h-5 w-5 text-muted-foreground" />
          <span className="text-sm font-medium text-muted-foreground">...</span>
        </div>
      )}

      {sortedEntries.length === 0 && (
        <div className="p-6 text-center text-sm text-muted-foreground border border-border rounded-md">
          This folder is empty
        </div>
      )}

      {sortedEntries.map((entry) => (
        <FileItem
          key={entry.oid}
          entry={entry}
          selected={selectedFile === entry.name}
          onClick={handleFileClick}
        />
      ))}

      {previewFile && (
        <div
          className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm p-2 sm:p-4"
          onClick={(e) => {
            if (e.target === e.currentTarget) {
              setPreviewFile(null);
            }
          }}
        >
          <div className="w-full max-w-5xl max-h-[90vh] flex flex-col bg-background rounded-lg shadow-xl overflow-hidden border border-border">
            <div className="flex-shrink-0 border-b border-border px-4 py-3">
              <div className="flex items-start sm:items-center justify-between gap-3">
                <div className="flex items-center gap-3 min-w-0">
                  <FileText className="h-5 w-5 text-muted-foreground shrink-0" />
                  <span className="text-base sm:text-lg truncate">{previewFile.name}</span>
                </div>
                <div className="flex items-center gap-1.5 flex-wrap justify-end shrink-0">
                  {/* Content / Chat tab toggle */}
                  <button
                    onClick={() => setPreviewTab('content')}
                    className={`px-2.5 py-1 text-xs border rounded-md transition-colors ${
                      previewTab === 'content'
                        ? 'bg-primary/10 text-primary border-primary/30'
                        : 'hover:bg-muted border-transparent'
                    }`}
                  >
                    Content
                  </button>
                  <button
                    onClick={() => setPreviewTab('chat')}
                    className={`px-2.5 py-1 text-xs border rounded-md transition-colors flex items-center gap-1 ${
                      previewTab === 'chat'
                        ? 'bg-primary/10 text-primary border-primary/30'
                        : 'hover:bg-muted border-transparent'
                    }`}
                  >
                    <MessageSquare className="h-3 w-3" />
                    Chat
                  </button>
                  <div className="w-px h-5 bg-border mx-1" />
                  <button
                    onClick={handleCopy}
                    disabled={blobLoading}
                    className="px-3 py-1 text-sm border rounded-md hover:bg-muted disabled:opacity-50"
                  >
                    Copy
                  </button>
                  <button
                    onClick={handleDownload}
                    disabled={blobLoading}
                    className="px-3 py-1 text-sm border rounded-md hover:bg-muted flex items-center gap-1"
                  >
                    <Download className="h-4 w-4" />
                    Download
                  </button>
                  <button
                    onClick={() => handlePreviewFile(null)}
                    className="p-1.5 border rounded-md hover:bg-muted"
                  >
                    <X className="h-4 w-4" />
                  </button>
                </div>
              </div>
              <div className="text-xs sm:text-sm text-muted-foreground mt-2 break-all">
                {previewFile.path}
              </div>
            </div>

            {previewTab === 'content' ? (
              <div className="flex-1 overflow-auto p-3 sm:p-6">
                {blobLoading ? (
                  <div className="flex items-center justify-center py-12">
                    <Loader2 className="h-8 w-8 animate-spin" />
                  </div>
                ) : !blobContent ? (
                  <div className="flex items-center justify-center py-12">
                    <div className="text-center">
                      <FileText className="h-12 w-12 text-muted-foreground/50 mx-auto mb-4" />
                      <p className="text-lg font-medium">Empty file</p>
                      <p className="text-sm text-muted-foreground">This file has no content</p>
                    </div>
                  </div>
                ) : (
                  <pre className="text-sm font-mono bg-muted/50 p-3 sm:p-4 rounded-lg overflow-x-auto max-w-full whitespace-pre-wrap">
                    <ReactMarkdown remarkPlugins={[remarkGfm]}>
                      {blobContent}
                    </ReactMarkdown>
                  </pre>
                )}
              </div>
            ) : (
              <div className="flex-1 overflow-hidden p-0">
                {repo && (
                  <MiniChat
                    roomId={`repo:${repo.namespace}:${repo.repo_name}:${previewFile.path}`}
                    repoBaseUrl={`/repository/${repo.namespace}/${repo.repo_name}`}
                    branch={branch ?? 'main'}
                    maxHeight={400}
                    className="h-full rounded-none border-0"
                  />
                )}
              </div>
            )}
          </div>
        </div>
      )}
    </div>
  );
};

interface FileItemProps {
  entry: TreeEntry;
  selected: boolean;
  onClick: (entry: TreeEntry) => void;
}

const FileItem = React.memo(({ entry, selected, onClick }: FileItemProps) => {
  const isDir = entry.kind === "tree";

  return (
    <div
      className={`flex items-center gap-3 px-4 py-2 border border-border rounded-md cursor-pointer transition-colors ${
        selected ? "bg-muted" : "hover:bg-muted"
      }`}
      onClick={() => onClick(entry)}
    >
      <div className="flex-shrink-0">
        {isDir ? (
          <FolderIcon className="h-5 w-5 text-blue-500" />
        ) : (
          <FileIcon className="h-5 w-5 text-muted-foreground" />
        )}
      </div>

      <div className="flex-shrink-0 min-w-[120px]">
        <span className="text-sm font-medium">{entry.name}</span>
        <span className="text-xs ml-1 font-normal text-muted-foreground border border-border px-1 rounded">
          {isDir ? "tree" : entry.is_binary ? "binary" : "blob"}
        </span>
      </div>

      <div className="flex-1 min-w-0 px-2" />

      <div className="flex-shrink-0">
        <span className="text-xs text-muted-foreground font-mono">{entry.oid.substring(0, 7)}</span>
      </div>
    </div>
  );
});

FileItem.displayName = "FileItem";

// Helper function to get tree entries for a given path
async function getTreeEntries(
  namespace: string,
  repo: string,
  rootTreeId: string,
  path: string
): Promise<TreeEntry[]> {
  if (!path) {
    // Root level - use root tree
    const resp = await gitTreeList({ path: { namespace, repo, oid: rootTreeId } });
    return resp.data?.data ?? [];
  }

  // For subdirectories, we need to walk the path
  // Split path into segments and recursively get entries
  const segments = path.split("/").filter(Boolean);
  let currentTreeId = rootTreeId;

  for (let i = 0; i < segments.length; i++) {
    const segment = segments[i];
    // Get entries for current tree
    const entriesResp = await gitTreeList({ path: { namespace, repo, oid: currentTreeId } });
    const entries = entriesResp.data?.data ?? [];

    // Find the directory we need
    const dirEntry = entries.find((e) => e.name === segment && e.kind === "tree");
    if (!dirEntry) {
      // Path not found
      return [];
    }
    currentTreeId = dirEntry.oid;

    // If this is the final segment, return its entries
    if (i === segments.length - 1) {
      const finalResp = await gitTreeList({ path: { namespace, repo, oid: currentTreeId } });
      return finalResp.data?.data ?? [];
    }
  }

  return [];
}
