import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { toast } from "sonner";
import { Loader2 } from "lucide-react";
import { trpc } from "@/lib/trpc";
import { parseUnifiedDiff } from "./parse-diff";
import { DiffViewer } from "./DiffViewer";
import { ReviewSidebar } from "./ReviewSidebar";
import { ReviewHeader } from "./ReviewHeader";
import { InitiativeReview } from "./InitiativeReview";
import type { ReviewStatus, DiffLine } from "./types";

interface ReviewTabProps {
  initiativeId: string;
}

export function ReviewTab({ initiativeId }: ReviewTabProps) {
  const [status, setStatus] = useState<ReviewStatus>("pending");
  const [selectedCommit, setSelectedCommit] = useState<string | null>(null);
  const [viewedFiles, setViewedFiles] = useState<Set<string>>(new Set());
  const fileRefs = useRef<Map<string, HTMLDivElement>>(new Map());
  const headerRef = useRef<HTMLDivElement>(null);
  const [headerHeight, setHeaderHeight] = useState(0);

  useEffect(() => {
    const el = headerRef.current;
    if (!el) return;
    const ro = new ResizeObserver(([entry]) => {
      setHeaderHeight(entry.borderBoxSize?.[0]?.blockSize ?? entry.target.getBoundingClientRect().height);
    });
    ro.observe(el, { box: 'border-box' });
    return () => ro.disconnect();
  }, []);

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

  const registerFileRef = useCallback((filePath: string, el: HTMLDivElement | null) => {
    if (el) {
      fileRefs.current.set(filePath, el);
    } else {
      fileRefs.current.delete(filePath);
    }
  }, []);

  // Fetch initiative to check for initiative-level pending_review
  const initiativeQuery = trpc.getInitiative.useQuery({ id: initiativeId });
  const isInitiativePendingReview = initiativeQuery.data?.status === "pending_review";

  // Fetch phases for this initiative
  const phasesQuery = trpc.listPhases.useQuery({ initiativeId });
  const reviewablePhases = useMemo(
    () => (phasesQuery.data ?? []).filter((p) => p.status === "pending_review" || p.status === "completed"),
    [phasesQuery.data],
  );

  // Select first pending review phase, falling back to completed phases
  const [selectedPhaseId, setSelectedPhaseId] = useState<string | null>(null);
  const defaultPhaseId = reviewablePhases.find((p) => p.status === "pending_review")?.id ?? reviewablePhases[0]?.id ?? null;
  const activePhaseId = selectedPhaseId ?? defaultPhaseId;
  const activePhase = reviewablePhases.find((p) => p.id === activePhaseId);
  const isActivePhaseCompleted = activePhase?.status === "completed";

  // Fetch projects for this initiative (needed for preview)
  const projectsQuery = trpc.getInitiativeProjects.useQuery({ initiativeId });
  const firstProjectId = projectsQuery.data?.[0]?.id ?? null;

  // Fetch full branch diff for active phase
  const diffQuery = trpc.getPhaseReviewDiff.useQuery(
    { phaseId: activePhaseId! },
    { enabled: !!activePhaseId && !isInitiativePendingReview },
  );

  // Fetch commits for active phase
  const commitsQuery = trpc.getPhaseReviewCommits.useQuery(
    { phaseId: activePhaseId! },
    { enabled: !!activePhaseId && !isInitiativePendingReview },
  );
  const commits = commitsQuery.data?.commits ?? [];

  // Fetch single-commit diff when a commit is selected
  const commitDiffQuery = trpc.getCommitDiff.useQuery(
    { phaseId: activePhaseId!, commitHash: selectedCommit! },
    { enabled: !!activePhaseId && !!selectedCommit && !isInitiativePendingReview },
  );

  // Preview state
  const previewsQuery = trpc.listPreviews.useQuery({ initiativeId });
  const existingPreview = previewsQuery.data?.find(
    (p) => p.phaseId === activePhaseId || p.initiativeId === initiativeId,
  );
  const [activePreviewId, setActivePreviewId] = useState<string | null>(null);
  const previewStatusQuery = trpc.getPreviewStatus.useQuery(
    { previewId: activePreviewId ?? existingPreview?.id ?? "" },
    { enabled: !!(activePreviewId ?? existingPreview?.id) },
  );
  const preview = previewStatusQuery.data ?? existingPreview;
  const sourceBranch = diffQuery.data?.sourceBranch ?? commitsQuery.data?.sourceBranch ?? "";

  const startPreview = trpc.startPreview.useMutation({
    onSuccess: (data) => {
      setActivePreviewId(data.id);
      previewsQuery.refetch();
      toast.success(`Preview running at ${data.url}`);
    },
    onError: (err) => toast.error(`Preview failed: ${err.message}`),
  });

  const stopPreview = trpc.stopPreview.useMutation({
    onSuccess: () => {
      setActivePreviewId(null);
      toast.success("Preview stopped");
      previewsQuery.refetch();
    },
    onError: (err) => toast.error(`Failed to stop: ${err.message}`),
  });

  const previewState = firstProjectId && sourceBranch
    ? {
        status: preview?.status === "running"
          ? ("running" as const)
          : preview?.status === "failed"
            ? ("failed" as const)
            : (startPreview.isPending || preview?.status === "building")
              ? ("building" as const)
              : ("idle" as const),
        url: preview?.url ?? undefined,
        onStart: () =>
          startPreview.mutate({
            initiativeId,
            phaseId: activePhaseId ?? undefined,
            projectId: firstProjectId,
            branch: sourceBranch,
          }),
        onStop: () => {
          const id = activePreviewId ?? existingPreview?.id;
          if (id) stopPreview.mutate({ previewId: id });
        },
        isStarting: startPreview.isPending,
        isStopping: stopPreview.isPending,
      }
    : null;

  // Review comments — persisted to DB
  const utils = trpc.useUtils();
  const commentsQuery = trpc.listReviewComments.useQuery(
    { phaseId: activePhaseId! },
    { enabled: !!activePhaseId && !isInitiativePendingReview },
  );
  const comments = useMemo(() => {
    return (commentsQuery.data ?? []).map((c) => ({
      id: c.id,
      filePath: c.filePath,
      lineNumber: c.lineNumber,
      lineType: c.lineType as "added" | "removed" | "context",
      body: c.body,
      author: c.author,
      createdAt: typeof c.createdAt === 'string' ? c.createdAt : String(c.createdAt),
      resolved: c.resolved,
      parentCommentId: c.parentCommentId ?? null,
    }));
  }, [commentsQuery.data]);

  const createCommentMutation = trpc.createReviewComment.useMutation({
    onSuccess: () => {
      utils.listReviewComments.invalidate({ phaseId: activePhaseId! });
    },
    onError: (err) => toast.error(`Failed to save comment: ${err.message}`),
  });

  const resolveCommentMutation = trpc.resolveReviewComment.useMutation({
    onSuccess: () => {
      utils.listReviewComments.invalidate({ phaseId: activePhaseId! });
    },
  });

  const unresolveCommentMutation = trpc.unresolveReviewComment.useMutation({
    onSuccess: () => {
      utils.listReviewComments.invalidate({ phaseId: activePhaseId! });
    },
  });

  const replyToCommentMutation = trpc.replyToReviewComment.useMutation({
    onSuccess: () => {
      utils.listReviewComments.invalidate({ phaseId: activePhaseId! });
    },
    onError: (err) => toast.error(`Failed to post reply: ${err.message}`),
  });

  const editCommentMutation = trpc.updateReviewComment.useMutation({
    onSuccess: () => {
      utils.listReviewComments.invalidate({ phaseId: activePhaseId! });
    },
    onError: (err) => toast.error(`Failed to update comment: ${err.message}`),
  });

  const approveMutation = trpc.approvePhaseReview.useMutation({
    onSuccess: () => {
      setStatus("approved");
      toast.success("Phase approved and merged");
      phasesQuery.refetch();
    },
    onError: (err) => toast.error(err.message),
  });

  // Determine which diff to display
  const activeDiffRaw = selectedCommit
    ? commitDiffQuery.data?.rawDiff
    : diffQuery.data?.rawDiff;

  const files = useMemo(() => {
    if (!activeDiffRaw) return [];
    return parseUnifiedDiff(activeDiffRaw);
  }, [activeDiffRaw]);

  const isDiffLoading = selectedCommit
    ? commitDiffQuery.isLoading
    : diffQuery.isLoading;

  const handleAddComment = useCallback(
    (filePath: string, lineNumber: number, lineType: DiffLine["type"], body: string) => {
      if (!activePhaseId) return;
      createCommentMutation.mutate({
        phaseId: activePhaseId,
        filePath,
        lineNumber,
        lineType,
        body,
      });
      toast.success("Comment added");
    },
    [activePhaseId, createCommentMutation],
  );

  const handleResolveComment = useCallback((commentId: string) => {
    resolveCommentMutation.mutate({ id: commentId });
  }, [resolveCommentMutation]);

  const handleUnresolveComment = useCallback((commentId: string) => {
    unresolveCommentMutation.mutate({ id: commentId });
  }, [unresolveCommentMutation]);

  const handleReplyComment = useCallback((parentCommentId: string, body: string) => {
    replyToCommentMutation.mutate({ parentCommentId, body });
  }, [replyToCommentMutation]);

  const handleEditComment = useCallback((commentId: string, body: string) => {
    editCommentMutation.mutate({ id: commentId, body });
  }, [editCommentMutation]);

  const handleApprove = useCallback(() => {
    if (!activePhaseId) return;
    approveMutation.mutate({ phaseId: activePhaseId });
  }, [activePhaseId, approveMutation]);

  const requestChangesMutation = trpc.requestPhaseChanges.useMutation({
    onSuccess: () => {
      setStatus("changes_requested");
      toast.success("Changes requested — revision task dispatched");
      phasesQuery.refetch();
    },
    onError: (err) => toast.error(err.message),
  });

  const handleRequestChanges = useCallback(() => {
    if (!activePhaseId) return;
    requestChangesMutation.mutate({ phaseId: activePhaseId });
  }, [activePhaseId, requestChangesMutation]);

  const handleFileClick = useCallback((filePath: string) => {
    const el = fileRefs.current.get(filePath);
    if (el) {
      el.scrollIntoView({ behavior: "smooth", block: "start" });
    }
  }, []);

  const handleCommentClick = useCallback((commentId: string) => {
    const el = document.querySelector(`[data-comment-id="${commentId}"]`);
    if (el) {
      el.scrollIntoView({ behavior: "instant", block: "center" });
      // Brief highlight flash
      el.classList.add("ring-2", "ring-primary/50");
      setTimeout(() => el.classList.remove("ring-2", "ring-primary/50"), 1500);
    }
  }, []);

  const handlePhaseSelect = useCallback((id: string) => {
    setSelectedPhaseId(id);
    setSelectedCommit(null);
    setStatus("pending");
    setViewedFiles(new Set());
  }, []);

  const unresolvedCount = comments.filter((c) => !c.resolved && !c.parentCommentId).length;

  const activePhaseName =
    diffQuery.data?.phaseName ??
    reviewablePhases.find((p) => p.id === activePhaseId)?.name ??
    "Phase";

  // All files from the full branch diff (for sidebar file list)
  const allFiles = useMemo(() => {
    if (!diffQuery.data?.rawDiff) return [];
    return parseUnifiedDiff(diffQuery.data.rawDiff);
  }, [diffQuery.data?.rawDiff]);

  // Initiative-level review takes priority
  if (isInitiativePendingReview) {
    return (
      <InitiativeReview
        initiativeId={initiativeId}
        onCompleted={() => {
          initiativeQuery.refetch();
          phasesQuery.refetch();
        }}
      />
    );
  }

  if (reviewablePhases.length === 0) {
    return (
      <div className="flex h-64 items-center justify-center text-muted-foreground">
        <p>No phases pending review</p>
      </div>
    );
  }

  return (
    <div
      className="rounded-lg border border-border bg-card"
      style={{ '--review-header-h': `${headerHeight}px` } as React.CSSProperties}
    >
      {/* Header: phase selector + toolbar */}
      <ReviewHeader
        ref={headerRef}
        phases={reviewablePhases.map((p) => ({ id: p.id, name: p.name, status: p.status }))}
        activePhaseId={activePhaseId}
        isReadOnly={isActivePhaseCompleted}
        onPhaseSelect={handlePhaseSelect}
        phaseName={activePhaseName}
        sourceBranch={sourceBranch}
        targetBranch={diffQuery.data?.targetBranch ?? commitsQuery.data?.targetBranch ?? ""}
        files={allFiles}
        status={status}
        unresolvedCount={unresolvedCount}
        onApprove={handleApprove}
        onRequestChanges={handleRequestChanges}
        isRequestingChanges={requestChangesMutation.isPending}
        preview={previewState}
        viewedCount={viewedFiles.size}
        totalCount={allFiles.length}
      />

      {/* Main content area — sidebar always rendered to preserve state */}
      <div className="grid grid-cols-1 lg:grid-cols-[260px_1fr] rounded-b-lg">
        {/* Left: Sidebar — sticky to viewport, scrolls independently */}
        <div className="border-r border-border">
          <div
            className="sticky overflow-hidden"
            style={{
              top: `${headerHeight}px`,
              maxHeight: `calc(100vh - ${headerHeight}px)`,
            }}
          >
            <ReviewSidebar
              files={allFiles}
              comments={comments}
              onFileClick={handleFileClick}
              onCommentClick={handleCommentClick}
              selectedCommit={selectedCommit}
              activeFiles={files}
              commits={commits}
              onSelectCommit={setSelectedCommit}
              viewedFiles={viewedFiles}
            />
          </div>
        </div>

        {/* Right: Diff */}
        <div className="min-w-0 p-4">
          {isDiffLoading ? (
            <div className="flex h-64 items-center justify-center text-muted-foreground gap-2">
              <Loader2 className="h-4 w-4 animate-spin" />
              Loading diff...
            </div>
          ) : files.length === 0 ? (
            <div className="flex h-32 items-center justify-center text-muted-foreground text-sm">
              {selectedCommit
                ? "No changes in this commit"
                : "No changes in this phase"}
            </div>
          ) : (
            <DiffViewer
              files={files}
              comments={comments}
              onAddComment={handleAddComment}
              onResolveComment={handleResolveComment}
              onUnresolveComment={handleUnresolveComment}
              onReplyComment={handleReplyComment}
              onEditComment={handleEditComment}
              viewedFiles={viewedFiles}
              onToggleViewed={toggleViewed}
              onRegisterRef={registerFileRef}
            />
          )}
        </div>
      </div>
    </div>
  );
}
