import { useState } from "react";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import {
  prDiffSideBySide,
  reviewCommentCreate,
  reviewCommentList,
  type SideBySideDiffResponse,
  type SideBySideFileResponse,
  type ReviewCommentResponse,
} from "@/client";
import { PRInlineComment } from "./PRInlineComment";
import { PRCommentInput } from "./PRCommentInput";
import {
  ChevronDown,
  ChevronRight,
  FileText,
  GitCompare,
  Plus,
  Loader2,
  File,
} from "lucide-react";
import { toast } from "sonner";

interface PRDiffViewerProps {
  namespace: string;
  repoName: string;
  prNumber: number;
  /** Base commit OID */
  base: string;
  /** Head commit OID */
  head: string;
}

type LineCommentMap = Map<number, ReviewCommentResponse[]>;

function buildLineCommentMap(comments: ReviewCommentResponse[]): LineCommentMap {
  const map = new Map<number, ReviewCommentResponse[]>();
  for (const c of comments) {
    if (c.path && c.line) {
      const key = c.line;
      if (!map.has(key)) map.set(key, []);
      map.get(key)!.push(c);
    }
  }
  return map;
}

const CHANGE_CLASS: Record<string, string> = {
  added: "bg-green-50 dark:bg-green-950/20",
  removed: "bg-red-50 dark:bg-red-950/20",
  modified: "bg-yellow-50 dark:bg-yellow-950/20",
  unchanged: "",
  empty: "bg-muted/30",
};

const LINE_NUM_CLASS: Record<string, string> = {
  added: "bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-400",
  removed: "bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-400",
  modified: "bg-yellow-100 dark:bg-yellow-900/30 text-yellow-700 dark:text-yellow-400",
  unchanged: "text-muted-foreground",
  empty: "text-muted-foreground",
};

function FileSection({
  file,
  commentMap,
  namespace,
  repoName,
  prNumber,
  onRefresh,
}: {
  file: SideBySideFileResponse;
  commentMap: LineCommentMap;
  namespace: string;
  repoName: string;
  prNumber: number;
  onRefresh: () => void;
}) {
  const [collapsed, setCollapsed] = useState(false);
  const [addingComment, setAddingComment] = useState<{
    lineNo: number;
    side: "LEFT" | "RIGHT";
    path: string;
  } | null>(null);

  const queryClient = useQueryClient();
  const addCommentMutation = useMutation({
    mutationFn: async ({
      body,
      lineNo,
      side,
      path,
    }: {
      body: string;
      lineNo: number;
      side: "LEFT" | "RIGHT";
      path: string;
    }) => {
      await reviewCommentCreate({
        path: { namespace, repo: repoName, pr_number: prNumber },
        body: {
          body,
          path,
          side,
          line: lineNo,
          old_line: null,
        },
      });
    },
    onSuccess: () => {
      setAddingComment(null);
      queryClient.invalidateQueries({ queryKey: ["pr-comments", namespace, repoName, prNumber] });
      toast.success("Comment added");
      onRefresh();
    },
    onError: () => toast.error("Failed to add comment"),
  });

  if (file.is_binary) {
    return (
      <div className="border rounded-md mb-2">
        <div className="flex items-center gap-2 p-3 bg-muted/30">
          <File className="h-4 w-4 text-muted-foreground" />
          <span className="text-sm font-medium font-mono">{file.path}</span>
          <span className="text-xs text-muted-foreground">Binary file</span>
        </div>
      </div>
    );
  }

  return (
    <div className="border rounded-md mb-3 overflow-hidden">
      {/* File header */}
      <button
        className="flex items-center gap-2 w-full p-3 bg-muted/30 hover:bg-muted/50 transition-colors text-left"
        onClick={() => setCollapsed(!collapsed)}
      >
        {collapsed ? (
          <ChevronRight className="h-4 w-4 flex-shrink-0 text-muted-foreground" />
        ) : (
          <ChevronDown className="h-4 w-4 flex-shrink-0 text-muted-foreground" />
        )}
        <FileText className="h-4 w-4 flex-shrink-0 text-muted-foreground" />
        <span className="text-sm font-medium font-mono">{file.path}</span>
        <div className="ml-auto flex items-center gap-2">
          {file.is_rename && (
            <span className="text-xs bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-400 px-1.5 py-0.5 rounded">
              renamed
            </span>
          )}
          {file.additions > 0 && (
            <span className="text-xs text-green-600 font-medium">
              +{file.additions}
            </span>
          )}
          {file.deletions > 0 && (
            <span className="text-xs text-red-600 font-medium">
              -{file.deletions}
            </span>
          )}
        </div>
      </button>

      {/* Collapsible content */}
      {!collapsed && (
        <div>
          {/* Column headers */}
          <div className="flex text-xs font-mono text-muted-foreground border-b bg-muted/20">
            <div className="w-12 flex-shrink-0 text-right pr-2 py-1 select-none">
              {file.lines.filter((l) => l.change_type !== "added").length > 0 ? "old" : ""}
            </div>
            <div className="w-12 flex-shrink-0 text-right pr-2 py-1 select-none">
              {file.lines.filter((l) => l.change_type !== "removed").length > 0 ? "new" : ""}
            </div>
            <div className="flex-1" />
          </div>

          {/* Lines with inline comments */}
          <div className="divide-y divide-border/50">
            {file.lines.map((line, idx) => {
              const lineComments = line.right_line_no ? commentMap.get(line.right_line_no) : undefined;
              return (
                <div key={idx}>
                  <div className="flex text-xs font-mono leading-6">
                    {/* Left side */}
                    <div className={`w-12 flex-shrink-0 text-right pr-2 ${LINE_NUM_CLASS[line.change_type] ?? ""}`}>
                      {line.left_line_no ?? ""}
                    </div>
                    {/* Right side */}
                    <div className={`w-12 flex-shrink-0 text-right pr-2 ${LINE_NUM_CLASS[line.change_type] ?? ""}`}>
                      {line.right_line_no ?? ""}
                    </div>
                    {/* Content */}
                    <div className={`flex-1 pl-2 pr-1 break-all whitespace-pre ${CHANGE_CLASS[line.change_type] ?? ""}`}>
                      {line.left_content || line.right_content || " "}
                    </div>
                    {/* Add comment (right side for added/modified lines) */}
                    {line.right_line_no && (
                      <button
                        className="w-8 flex-shrink-0 flex items-center justify-center opacity-0 group-hover:opacity-100 hover:text-blue-600 transition-opacity"
                        onClick={() =>
                          setAddingComment({
                            lineNo: line.right_line_no!,
                            side: "RIGHT",
                            path: file.path,
                          })
                        }
                        title="Add a comment"
                      >
                        <Plus className="h-3.5 w-3.5" />
                      </button>
                    )}
                  </div>
                  {/* Inline comments for this line */}
                  {lineComments && lineComments.length > 0 && (
                    <div className="px-4 pb-2 bg-blue-50/30 dark:bg-blue-950/10 border-l-2 border-blue-400">
                      {lineComments.map((comment) => (
                        <PRInlineComment
                          key={comment.id}
                          comment={comment}
                          namespace={namespace}
                          repoName={repoName}
                          prNumber={prNumber}
                          onRefresh={onRefresh}
                        />
                      ))}
                    </div>
                  )}
                </div>
              );
            })}
          </div>

          {/* Adding comment form */}
          {addingComment && (
            <div className="p-3 bg-blue-50 dark:bg-blue-950/20 border-t">
              <p className="text-xs text-muted-foreground mb-2">
                Commenting on line {addingComment.lineNo} of {addingComment.path}
              </p>
              <PRCommentInput
                placeholder="Leave a comment..."
                buttonLabel="Add comment"
                onSubmit={async (body) => {
                  await addCommentMutation.mutateAsync({
                    body,
                    lineNo: addingComment.lineNo,
                    side: addingComment.side,
                    path: addingComment.path,
                  });
                }}
                onCancel={() => setAddingComment(null)}
                autoFocus
              />
            </div>
          )}
        </div>
      )}
    </div>
  );
}

export function PRDiffViewer({
  namespace,
  repoName,
  prNumber,
  base,
  head,
}: PRDiffViewerProps) {
  const queryClient = useQueryClient();

  const { data: diffData, isLoading: diffLoading } = useQuery({
    queryKey: ["pr-diff-side-by-side", namespace, repoName, prNumber, base, head],
    queryFn: async () => {
      const resp = await prDiffSideBySide({
        path: { namespace, repo: repoName, pr_number: prNumber },
      });
      return resp.data?.data as SideBySideDiffResponse;
    },
    enabled: !!namespace && !!repoName && !!prNumber && !!base && !!head,
    staleTime: 60 * 1000,
  });

  const { data: commentsData } = useQuery({
    queryKey: ["pr-comments", namespace, repoName, prNumber],
    queryFn: async () => {
      const resp = await reviewCommentList({
        path: { namespace, repo: repoName, pr_number: prNumber },
        query: {},
      });
      return resp.data?.data;
    },
    enabled: !!namespace && !!repoName && !!prNumber,
    staleTime: 30 * 1000,
  });

  const comments = commentsData?.comments ?? [];
  const commentMap = buildLineCommentMap(comments);

  const handleRefresh = () => {
    queryClient.invalidateQueries({ queryKey: ["pr-comments", namespace, repoName, prNumber] });
  };

  if (diffLoading) {
    return (
      <div className="flex items-center justify-center h-64">
        <Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
        <span className="ml-2 text-sm text-muted-foreground">Loading diff...</span>
      </div>
    );
  }

  if (!diffData) {
    return (
      <div className="flex items-center justify-center h-32 text-muted-foreground">
        No diff data available.
      </div>
    );
  }

  return (
    <div className="space-y-1">
      {/* Summary bar */}
      <div className="flex items-center gap-4 py-2 px-1">
        <div className="flex items-center gap-3 text-sm">
          <span className="flex items-center gap-1">
            <GitCompare className="h-4 w-4 text-muted-foreground" />
            <span className="text-muted-foreground">{diffData.files.length} file(s)</span>
          </span>
          <span className="text-green-600 font-medium">
            +{diffData.total_additions}
          </span>
          <span className="text-red-600 font-medium">
            -{diffData.total_deletions}
          </span>
        </div>
      </div>

      {/* File sections */}
      {diffData.files.map((file, idx) => (
        <FileSection
          key={`${file.path}-${idx}`}
          file={file}
          commentMap={commentMap}
          namespace={namespace}
          repoName={repoName}
          prNumber={prNumber}
          onRefresh={handleRefresh}
        />
      ))}
    </div>
  );
}
