import Logo from "@/components/Logo";
import NewReviewData from "@/components/dynamic/NewReviewData";
import CalendarFilterButton from "@/components/filter/CalendarFilterButton";
import ReviewActionGroup from "@/components/filter/ReviewActionGroup";
import ReviewFilterGroup from "@/components/filter/ReviewFilterGroup";
import PreviewThumbnailPlayer from "@/components/player/PreviewThumbnailPlayer";
import EventReviewTimeline from "@/components/timeline/EventReviewTimeline";
import ActivityIndicator from "@/components/indicators/activity-indicator";
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
import { VolumeSlider } from "@/components/ui/slider";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectSeparator,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import { useTimelineUtils } from "@/hooks/use-timeline-utils";
import { useScrollLockout } from "@/hooks/use-mouse-listener";
import { FrigateConfig } from "@/types/frigateConfig";
import { Preview } from "@/types/preview";
import {
  MotionData,
  RecordingsSummary,
  REVIEW_PADDING,
  ReviewFilter,
  ReviewSegment,
  ReviewSeverity,
  ReviewSummary,
  SegmentedReviewData,
  ZoomLevel,
} from "@/types/review";
import { getChunkedTimeRange } from "@/utils/timelineUtil";
import { isReplayCamera } from "@/utils/cameraUtil";
import { getEndOfDayTimestamp } from "@/utils/dateUtil";
import axios from "axios";
import {
  MutableRefObject,
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from "react";
import { isDesktop, isMobile, isMobileOnly } from "react-device-detect";
import { LuFolderCheck, LuFolderX } from "react-icons/lu";
import { MdCircle } from "react-icons/md";
import { FiMoreVertical } from "react-icons/fi";
import { IoMdArrowRoundBack } from "react-icons/io";
import useSWR from "swr";
import MotionReviewTimeline from "@/components/timeline/MotionReviewTimeline";
import { baseUrl } from "@/api/baseUrl";
import { Button } from "@/components/ui/button";
import BlurredIconButton from "@/components/button/BlurredIconButton";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import PreviewPlayer, {
  PreviewController,
} from "@/components/player/PreviewPlayer";
import SummaryTimeline from "@/components/timeline/SummaryTimeline";
import { RecordingStartingPoint } from "@/types/record";
import VideoControls from "@/components/player/VideoControls";
import { TimeRange } from "@/types/timeline";
import { useAllowedCameras } from "@/hooks/use-allowed-cameras";
import {
  useCameraMotionNextTimestamp,
  useCameraMotionOnlyRanges,
} from "@/hooks/use-camera-activity";
import useOptimisticState from "@/hooks/use-optimistic-state";
import { Skeleton } from "@/components/ui/skeleton";
import scrollIntoView from "scroll-into-view-if-needed";
import { Toaster } from "@/components/ui/sonner";
import { toast } from "sonner";
import { cn } from "@/lib/utils";
import { FilterList, LAST_24_HOURS_KEY } from "@/types/filter";
import { GiSoundWaves } from "react-icons/gi";
import useKeyboardListener from "@/hooks/use-keyboard-listener";
import { useTimelineZoom } from "@/hooks/use-timeline-zoom";
import { useTranslation } from "react-i18next";
import { FaCog, FaFilter } from "react-icons/fa";
import MotionRegionFilterGrid from "@/components/filter/MotionRegionFilterGrid";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog";
import ReviewActivityCalendar from "@/components/overlay/ReviewActivityCalendar";
import PlatformAwareDialog from "@/components/overlay/dialog/PlatformAwareDialog";
import MotionPreviewsPane from "./MotionPreviewsPane";
import { EmptyCard } from "@/components/card/EmptyCard";
import { EmptyCardData } from "@/types/card";

type EventViewProps = {
  reviewItems?: SegmentedReviewData;
  currentReviewItems: ReviewSegment[] | null;
  reviewSummary?: ReviewSummary;
  recordingsSummary?: RecordingsSummary;
  relevantPreviews?: Preview[];
  timeRange: TimeRange;
  filter?: ReviewFilter;
  severity: ReviewSeverity;
  startTime?: number;
  showReviewed: boolean;
  setShowReviewed: (show: boolean) => void;
  setSeverity: (severity: ReviewSeverity) => void;
  markItemAsReviewed: (review: ReviewSegment) => void;
  markItemsAsReviewed: (
    currentItems: ReviewSegment[],
    itemsToMarkReviewed?: ReviewSegment[] | undefined,
  ) => void;
  onOpenRecording: (recordingInfo: RecordingStartingPoint) => void;
  motionPreviewsCamera: string | null;
  setMotionPreviewsCamera: (camera: string | null) => void;
  setMotionSearchCamera: (camera: string) => void;
  pullLatestData: () => void;
  updateFilter: (filter: ReviewFilter) => void;
};
export default function EventView({
  reviewItems,
  currentReviewItems,
  reviewSummary,
  recordingsSummary,
  relevantPreviews,
  timeRange,
  filter,
  severity,
  startTime,
  showReviewed,
  setShowReviewed,
  setSeverity,
  markItemAsReviewed,
  markItemsAsReviewed,
  onOpenRecording,
  motionPreviewsCamera,
  setMotionPreviewsCamera,
  setMotionSearchCamera,
  pullLatestData,
  updateFilter,
}: EventViewProps) {
  const { t } = useTranslation(["views/events"]);
  const { data: config } = useSWR<FrigateConfig>("config");
  const contentRef = useRef<HTMLDivElement | null>(null);

  // review counts

  const reviewCounts = useMemo(() => {
    if (!reviewSummary) {
      return { alert: -1, detection: -1, significant_motion: -1 };
    }

    let summary;
    if (filter?.before == undefined) {
      summary = reviewSummary[LAST_24_HOURS_KEY];
    } else {
      const day = new Date(filter.before * 1000);
      const key = `${day.getFullYear()}-${("0" + (day.getMonth() + 1)).slice(-2)}-${("0" + day.getDate()).slice(-2)}`;
      summary = reviewSummary[key];
    }

    if (!summary) {
      return { alert: 0, detection: 0, significant_motion: 0 };
    }

    if (showReviewed) {
      return {
        alert: summary.total_alert ?? 0,
        detection: summary.total_detection ?? 0,
      };
    } else {
      return {
        alert: summary.total_alert - summary.reviewed_alert,
        detection: summary.total_detection - summary.reviewed_detection,
      };
    }
  }, [filter, showReviewed, reviewSummary]);

  const emptyCardData: EmptyCardData = useMemo(() => {
    if (
      !config ||
      Object.values(config.cameras).find(
        (cam) => cam.record.enabled_in_config,
      ) != undefined
    ) {
      return {
        title: t("empty." + severity.replace(/_/g, " ")),
      };
    }

    return {
      title: t("empty.recordingsDisabled.title"),
      description: t("empty.recordingsDisabled.description"),
    };
  }, [config, severity, t]);

  // review interaction

  const [selectedReviews, setSelectedReviews] = useState<ReviewSegment[]>([]);
  const onSelectReview = useCallback(
    (review: ReviewSegment, ctrl: boolean, detail: boolean) => {
      if (selectedReviews.length > 0 || ctrl) {
        const index = selectedReviews.findIndex((r) => r.id === review.id);

        if (index != -1) {
          if (selectedReviews.length == 1) {
            setSelectedReviews([]);
          } else {
            const copy = [
              ...selectedReviews.slice(0, index),
              ...selectedReviews.slice(index + 1),
            ];
            setSelectedReviews(copy);
          }
        } else {
          const copy = [...selectedReviews];
          copy.push(review);
          setSelectedReviews(copy);
        }
      } else {
        // If a specific date is selected in the calendar and it's after the event start,
        // use the selected date instead of the event start time
        const effectiveStartTime =
          timeRange.after > review.start_time
            ? timeRange.after
            : review.start_time;

        onOpenRecording({
          camera: review.camera,
          startTime: effectiveStartTime - REVIEW_PADDING,
          severity: review.severity,
          timelineType: detail ? "detail" : undefined,
        });

        review.has_been_reviewed = true;
        markItemAsReviewed(review);
      }
    },
    [
      selectedReviews,
      setSelectedReviews,
      onOpenRecording,
      markItemAsReviewed,
      timeRange.after,
    ],
  );
  const onSelectAllReviews = useCallback(() => {
    if (!currentReviewItems || currentReviewItems.length == 0) {
      return;
    }

    if (selectedReviews.length < currentReviewItems.length) {
      setSelectedReviews(currentReviewItems);
    } else {
      setSelectedReviews([]);
    }
  }, [currentReviewItems, selectedReviews]);

  const exportReview = useCallback(
    (id: string) => {
      const review = reviewItems?.all?.find((seg) => seg.id == id);

      if (!review) {
        return;
      }

      const endTime = review.end_time
        ? review.end_time + REVIEW_PADDING
        : Date.now() / 1000;

      axios
        .post(
          `export/${review.camera}/start/${review.start_time - REVIEW_PADDING}/end/${endTime}`,
          { playback: "realtime", image_path: review.thumb_path },
        )
        .then((response) => {
          if (response.status < 300) {
            toast.success(
              t("export.toast.success", { ns: "components/dialog" }),
              {
                position: "top-center",
                action: (
                  <a
                    href={`${baseUrl}export`}
                    target="_blank"
                    rel="noopener noreferrer"
                  >
                    <Button>
                      {t("export.toast.view", { ns: "components/dialog" })}
                    </Button>
                  </a>
                ),
              },
            );
          }
        })
        .catch((error) => {
          const errorMessage =
            error.response?.data?.message ||
            error.response?.data?.detail ||
            "Unknown error";
          toast.error(
            t("export.toast.error.failed", {
              ns: "components/dialog",
              message: errorMessage,
            }),
            {
              position: "top-center",
            },
          );
        });
    },
    [reviewItems, t],
  );

  const [motionOnly, setMotionOnly] = useState(false);
  const [severityToggle, setSeverityToggle] = useOptimisticState(
    severity,
    setSeverity,
    100,
  );

  const motionPreviewsOpen =
    severity === "significant_motion" && motionPreviewsCamera != null;

  useEffect(() => {
    if (severity !== "significant_motion") {
      setMotionPreviewsCamera(null);
    }
  }, [setMotionPreviewsCamera, severity]);

  // review filter info

  const reviewFilterList = useMemo<FilterList>(() => {
    const uniqueLabels = new Set<string>();
    const uniqueZones = new Set<string>();

    reviewItems?.all?.forEach((rev) => {
      rev.data.objects.forEach((obj) =>
        uniqueLabels.add(obj.replace("-verified", "")),
      );
      rev.data.audio.forEach((aud) => uniqueLabels.add(aud));
    });

    reviewItems?.all?.forEach((rev) => {
      rev.data.zones.forEach((zone) => uniqueZones.add(zone));
    });

    return { labels: [...uniqueLabels], zones: [...uniqueZones] };
  }, [reviewItems]);

  if (!config) {
    return <ActivityIndicator />;
  }

  return (
    <div className="flex size-full flex-col pt-2 md:py-2">
      <Toaster closeButton={true} />
      {!motionPreviewsOpen && (
        <div className="relative mb-2 flex h-11 items-center justify-between pl-2 pr-2 md:pl-3">
          {isMobile && (
            <Logo className="absolute inset-x-1/2 h-8 -translate-x-1/2" />
          )}
          <ToggleGroup
            className="*:rounded-md *:px-3 *:py-4"
            type="single"
            size="sm"
            value={severityToggle}
            onValueChange={(value: ReviewSeverity) =>
              value ? setSeverityToggle(value) : null
            } // don't allow the severity to be unselected
          >
            <ToggleGroupItem
              className={cn(
                severityToggle != "alert" && "text-muted-foreground",
              )}
              value="alert"
              aria-label={t("alerts")}
            >
              <div
                className={cn(
                  "flex size-6 items-center justify-center rounded text-severity_alert sm:hidden",
                  severityToggle == "alert" ? "font-semibold" : "font-medium",
                )}
              >
                {reviewCounts.alert > -1 ? (
                  reviewCounts.alert
                ) : (
                  <ActivityIndicator className="size-4" />
                )}
              </div>
              <div className="hidden items-center sm:flex">
                <MdCircle className="size-2 text-severity_alert md:mr-[10px]" />
                <div className="hidden md:flex md:flex-row md:items-center">
                  {t("alerts")}
                  {reviewCounts.alert > -1 ? (
                    ` ∙ ${reviewCounts.alert}`
                  ) : (
                    <ActivityIndicator className="ml-2 size-4" />
                  )}
                </div>
              </div>
            </ToggleGroupItem>
            <ToggleGroupItem
              className={cn(
                severityToggle != "detection" && "text-muted-foreground",
              )}
              value="detection"
              aria-label={t("detections")}
            >
              <div
                className={cn(
                  "flex size-6 items-center justify-center rounded text-severity_detection sm:hidden",
                  severityToggle == "detection"
                    ? "font-semibold"
                    : "font-medium",
                )}
              >
                {reviewCounts.detection > -1 ? (
                  reviewCounts.detection
                ) : (
                  <ActivityIndicator className="size-4" />
                )}
              </div>
              <div className="hidden items-center sm:flex">
                <MdCircle className="size-2 text-severity_detection md:mr-[10px]" />
                <div className="hidden md:flex md:flex-row md:items-center">
                  {t("detections")}
                  {reviewCounts.detection > -1 ? (
                    ` ∙ ${reviewCounts.detection}`
                  ) : (
                    <ActivityIndicator className="ml-2 size-4" />
                  )}
                </div>
              </div>
            </ToggleGroupItem>
            <ToggleGroupItem
              className={cn(
                "rounded-lg px-3 py-4",
                severityToggle != "significant_motion" &&
                  "text-muted-foreground",
              )}
              value="significant_motion"
              aria-label={t("motion.label")}
            >
              <GiSoundWaves className="size-6 rotate-90 text-severity_significant_motion sm:hidden" />
              <div className="hidden items-center sm:flex">
                <MdCircle className="size-2 text-severity_significant_motion md:mr-[10px]" />
                <div className="hidden md:block">{t("motion.label")}</div>
              </div>
            </ToggleGroupItem>
          </ToggleGroup>

          {selectedReviews.length <= 0 ? (
            <ReviewFilterGroup
              filters={
                severity == "significant_motion"
                  ? ["cameras", "date", "motionOnly"]
                  : ["cameras", "reviewed", "date", "general"]
              }
              currentSeverity={severityToggle}
              reviewSummary={reviewSummary}
              recordingsSummary={recordingsSummary}
              filter={filter}
              motionOnly={motionOnly}
              filterList={reviewFilterList}
              showReviewed={showReviewed}
              setShowReviewed={setShowReviewed}
              onUpdateFilter={updateFilter}
              setMotionOnly={setMotionOnly}
            />
          ) : (
            <ReviewActionGroup
              selectedReviews={selectedReviews}
              setSelectedReviews={setSelectedReviews}
              onExport={exportReview}
              pullLatestData={pullLatestData}
            />
          )}
        </div>
      )}

      <div
        className={cn(
          "h-full min-h-0 overflow-hidden",
          motionPreviewsOpen ? "flex flex-col" : "flex",
        )}
      >
        {severity != "significant_motion" && (
          <DetectionReview
            contentRef={contentRef}
            reviewItems={reviewItems}
            currentItems={currentReviewItems}
            relevantPreviews={relevantPreviews}
            selectedReviews={selectedReviews}
            itemsToReview={reviewCounts[severityToggle]}
            severity={severity}
            filter={filter}
            timeRange={timeRange}
            startTime={startTime}
            loading={severity != severityToggle}
            emptyCardData={emptyCardData}
            markItemAsReviewed={markItemAsReviewed}
            markItemsAsReviewed={markItemsAsReviewed}
            onSelectReview={onSelectReview}
            onSelectAllReviews={onSelectAllReviews}
            setSelectedReviews={setSelectedReviews}
            pullLatestData={pullLatestData}
          />
        )}
        {severity == "significant_motion" && (
          <MotionReview
            key={timeRange.before}
            contentRef={contentRef}
            reviewItems={reviewItems}
            relevantPreviews={relevantPreviews}
            reviewSummary={reviewSummary}
            recordingsSummary={recordingsSummary}
            timeRange={timeRange}
            startTime={startTime}
            filter={filter}
            motionOnly={motionOnly}
            updateFilter={updateFilter}
            motionPreviewsCamera={motionPreviewsCamera}
            setMotionPreviewsCamera={setMotionPreviewsCamera}
            setMotionSearchCamera={setMotionSearchCamera}
            emptyCardData={emptyCardData}
            onOpenRecording={onOpenRecording}
          />
        )}
      </div>
    </div>
  );
}

type DetectionReviewProps = {
  contentRef: MutableRefObject<HTMLDivElement | null>;
  reviewItems?: {
    all: ReviewSegment[];
    alert: ReviewSegment[];
    detection: ReviewSegment[];
    significant_motion: ReviewSegment[];
  };
  currentItems: ReviewSegment[] | null;
  itemsToReview?: number;
  relevantPreviews?: Preview[];
  selectedReviews: ReviewSegment[];
  severity: ReviewSeverity;
  filter?: ReviewFilter;
  timeRange: { before: number; after: number };
  startTime?: number;
  loading: boolean;
  emptyCardData: EmptyCardData;
  markItemAsReviewed: (review: ReviewSegment) => void;
  markItemsAsReviewed: (
    currentItems: ReviewSegment[],
    itemsToMarkReviewed?: ReviewSegment[] | undefined,
  ) => void;
  onSelectReview: (
    review: ReviewSegment,
    ctrl: boolean,
    detail: boolean,
  ) => void;
  onSelectAllReviews: () => void;
  setSelectedReviews: (reviews: ReviewSegment[]) => void;
  pullLatestData: () => void;
};
function DetectionReview({
  contentRef,
  reviewItems,
  currentItems,
  itemsToReview,
  relevantPreviews,
  selectedReviews,
  severity,
  filter,
  timeRange,
  startTime,
  loading,
  emptyCardData,
  markItemAsReviewed,
  markItemsAsReviewed,
  onSelectReview,
  onSelectAllReviews,
  setSelectedReviews,
  pullLatestData,
}: DetectionReviewProps) {
  const { t } = useTranslation(["views/events"]);

  const reviewTimelineRef = useRef<HTMLDivElement>(null);

  // preview

  const [previewTime, setPreviewTime] = useState<number>();

  const onPreviewTimeUpdate = useCallback(
    (time: number | undefined) => {
      if (!time) {
        setPreviewTime(time);
        return;
      }

      if (!previewTime || time > previewTime) {
        setPreviewTime(time);
      }
    },
    [previewTime, setPreviewTime],
  );

  // timeline interaction

  const timelineDuration = useMemo(
    () => timeRange.before - timeRange.after,
    [timeRange],
  );

  const [zoomSettings, setZoomSettings] = useState({
    segmentDuration: 60,
    timestampSpread: 15,
  });

  const possibleZoomLevels: ZoomLevel[] = useMemo(
    () => [
      { segmentDuration: 60, timestampSpread: 15 },
      { segmentDuration: 30, timestampSpread: 5 },
      { segmentDuration: 10, timestampSpread: 1 },
    ],
    [],
  );

  const handleZoomChange = useCallback(
    (newZoomLevel: number) => {
      setZoomSettings(possibleZoomLevels[newZoomLevel]);
    },
    [possibleZoomLevels],
  );

  const currentZoomLevel = useMemo(
    () =>
      possibleZoomLevels.findIndex(
        (level) => level.segmentDuration === zoomSettings.segmentDuration,
      ),
    [possibleZoomLevels, zoomSettings.segmentDuration],
  );

  const { isZooming, zoomDirection } = useTimelineZoom({
    zoomSettings,
    zoomLevels: possibleZoomLevels,
    onZoomChange: handleZoomChange,
    timelineRef: reviewTimelineRef,
    timelineDuration,
  });

  const { alignStartDateToTimeline, getVisibleTimelineDuration } =
    useTimelineUtils({
      segmentDuration: zoomSettings.segmentDuration,
      timelineDuration,
      timelineRef: reviewTimelineRef,
    });

  const scrollLock = useScrollLockout(contentRef);

  const [minimap, setMinimap] = useState<string[]>([]);
  const minimapObserver = useRef<IntersectionObserver | null>(null);
  useEffect(() => {
    const visibleTimestamps = new Set<string>();
    minimapObserver.current = new IntersectionObserver(
      (entries) => {
        entries.forEach((entry) => {
          const start = (entry.target as HTMLElement).dataset.start;

          if (!start) {
            return;
          }

          if (entry.isIntersecting) {
            visibleTimestamps.add(start);
          } else {
            visibleTimestamps.delete(start);
          }

          setMinimap([...visibleTimestamps]);
        });
      },
      { root: contentRef.current, threshold: isDesktop ? 0.1 : 0.5 },
    );

    return () => {
      minimapObserver.current?.disconnect();
    };
  }, [contentRef, minimapObserver]);

  const minimapBounds = useMemo(() => {
    const data = {
      start: 0,
      end: 0,
    };
    const list = minimap.sort();

    if (list.length > 0) {
      data.end = parseFloat(list.at(-1) || "0");
      data.start = parseFloat(list[0]);
    }

    return data;
  }, [minimap]);

  const minimapRef = useCallback(
    (node: HTMLElement | null) => {
      if (!minimapObserver.current) {
        return;
      }

      try {
        if (node) minimapObserver.current.observe(node);
      } catch (e) {
        // no op
      }
    },
    [minimapObserver],
  );

  const showMinimap = useMemo(() => {
    if (!contentRef.current) {
      return false;
    }

    // don't show minimap if the view is not scrollable
    if (contentRef.current.scrollHeight < contentRef.current.clientHeight) {
      return false;
    }

    const visibleTime = getVisibleTimelineDuration();
    const minimapTime = minimapBounds.end - minimapBounds.start;
    if (visibleTime && minimapTime >= visibleTime * 0.75) {
      return false;
    }

    return true;
    // we know that these deps are correct
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [contentRef.current?.scrollHeight, minimapBounds]);

  const visibleTimestamps = useMemo(
    () => minimap.map((str) => parseFloat(str)),
    [minimap],
  );

  // existing review item

  useEffect(() => {
    if (loading || currentItems == null || itemsToReview == undefined) {
      return;
    }

    if (currentItems.length == 0 && itemsToReview > 0) {
      pullLatestData();
    }
  }, [loading, currentItems, itemsToReview, pullLatestData]);

  useEffect(() => {
    if (!startTime || !currentItems || currentItems.length == 0) {
      return;
    }

    const element = contentRef.current?.querySelector(
      `[data-start="${startTime + REVIEW_PADDING}"]`,
    );
    if (element) {
      scrollIntoView(element, {
        scrollMode: "if-needed",
        behavior: "smooth",
      });
    }
    // only run when start time changes
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [startTime]);

  // keyboard

  useKeyboardListener(
    ["a", "r", "Escape"],
    (key, modifiers) => {
      if (!modifiers.down) {
        return true;
      }

      switch (key) {
        case "a":
          if (modifiers.ctrl && !modifiers.repeat) {
            onSelectAllReviews();
            return true;
          }
          break;
        case "r":
          if (selectedReviews.length > 0 && !modifiers.repeat) {
            markItemsAsReviewed(currentItems || [], selectedReviews);
            setSelectedReviews([]);
            return true;
          }
          break;
        case "Escape":
          setSelectedReviews([]);
          return true;
      }

      return false;
    },
    contentRef,
  );

  return (
    <>
      <div
        ref={contentRef}
        className="no-scrollbar flex flex-1 flex-wrap content-start gap-2 overflow-y-auto md:gap-4"
      >
        {filter?.before == undefined && (
          <NewReviewData
            className="pointer-events-none absolute left-1/2 z-[49] -translate-x-1/2"
            contentRef={contentRef}
            reviewItems={currentItems}
            itemsToReview={loading ? 0 : itemsToReview}
            pullLatestData={pullLatestData}
          />
        )}

        {!currentItems && (
          <div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2">
            <ActivityIndicator />
          </div>
        )}

        {!loading && currentItems?.length === 0 && (
          <EmptyCard
            className="absolute left-[50%] top-[50%] -translate-x-1/2 -translate-y-1/2 items-center text-center"
            title={emptyCardData.title}
            titleHeading={false}
            description={emptyCardData.description}
            icon={<LuFolderCheck className="size-16" />}
          />
        )}

        <div
          className="grid w-full gap-2 px-1 sm:grid-cols-2 md:mx-2 md:grid-cols-3 md:gap-4 3xl:grid-cols-4"
          ref={contentRef}
        >
          {!loading && currentItems
            ? currentItems.map((value) => {
                const selected = selectedReviews.some((r) => r.id === value.id);

                return (
                  <div
                    key={value.id}
                    ref={minimapRef}
                    data-start={value.start_time}
                    data-segment-start={
                      alignStartDateToTimeline(value.start_time) -
                      zoomSettings.segmentDuration
                    }
                    className="review-item relative rounded-lg"
                  >
                    <div className="aspect-video overflow-hidden rounded-lg">
                      <PreviewThumbnailPlayer
                        review={value}
                        allPreviews={relevantPreviews}
                        timeRange={timeRange}
                        setReviewed={markItemAsReviewed}
                        scrollLock={scrollLock}
                        onTimeUpdate={onPreviewTimeUpdate}
                        onClick={(
                          review: ReviewSegment,
                          ctrl: boolean,
                          detail: boolean,
                        ) => {
                          onSelectReview(review, ctrl, detail);
                        }}
                      />
                    </div>
                    <div
                      className={cn(
                        "review-item-ring pointer-events-none absolute inset-0 z-10 size-full rounded-lg outline outline-[3px] -outline-offset-[2.8px]",
                        selected
                          ? `outline-severity_${value.severity} shadow-severity_${value.severity}`
                          : "outline-transparent duration-500",
                      )}
                    />
                  </div>
                );
              })
            : (itemsToReview ?? 0) > 0 &&
              Array(itemsToReview)
                .fill(0)
                .map((_, idx) => (
                  <Skeleton key={idx} className="aspect-video size-full" />
                ))}
          {!loading &&
            (currentItems?.filter((seg) => seg.end_time)?.length ?? 0) > 0 &&
            (itemsToReview ?? 0) > 0 && (
              <div className="col-span-full flex items-center justify-center">
                <Button
                  className="text-balance text-white"
                  aria-label={t("markTheseItemsAsReviewed")}
                  variant="select"
                  onClick={() => {
                    setSelectedReviews([]);
                    markItemsAsReviewed(currentItems ?? []);
                  }}
                >
                  {t("markTheseItemsAsReviewed")}
                </Button>
              </div>
            )}
        </div>
      </div>
      <div className="flex w-[65px] flex-row md:w-[110px]">
        <div className="no-scrollbar relative w-[55px] md:w-[100px]">
          {loading ? (
            <Skeleton className="size-full" />
          ) : (
            <EventReviewTimeline
              segmentDuration={zoomSettings.segmentDuration}
              timestampSpread={zoomSettings.timestampSpread}
              timelineStart={timeRange.before}
              timelineEnd={timeRange.after}
              showMinimap={showMinimap && !previewTime}
              minimapStartTime={minimapBounds.start}
              minimapEndTime={minimapBounds.end}
              showHandlebar={previewTime != undefined}
              handlebarTime={previewTime}
              visibleTimestamps={visibleTimestamps}
              events={reviewItems?.all ?? []}
              severityType={severity}
              contentRef={contentRef}
              timelineRef={reviewTimelineRef}
              dense={isMobile}
              isZooming={isZooming}
              zoomDirection={zoomDirection}
              possibleZoomLevels={possibleZoomLevels}
              currentZoomLevel={currentZoomLevel}
            />
          )}
        </div>
        <div className="w-[10px]">
          {loading ? (
            <Skeleton className="w-full" />
          ) : (
            <SummaryTimeline
              reviewTimelineRef={reviewTimelineRef}
              timelineStart={timeRange.before}
              timelineEnd={timeRange.after}
              segmentDuration={zoomSettings.segmentDuration}
              events={reviewItems?.all ?? []}
              severityType={severity}
            />
          )}
        </div>
      </div>
    </>
  );
}

type MotionReviewProps = {
  contentRef: MutableRefObject<HTMLDivElement | null>;
  reviewItems?: {
    all: ReviewSegment[];
    alert: ReviewSegment[];
    detection: ReviewSegment[];
    significant_motion: ReviewSegment[];
  };
  relevantPreviews?: Preview[];
  reviewSummary?: ReviewSummary;
  recordingsSummary?: RecordingsSummary;
  timeRange: TimeRange;
  startTime?: number;
  filter?: ReviewFilter;
  motionOnly?: boolean;
  updateFilter: (filter: ReviewFilter) => void;
  motionPreviewsCamera: string | null;
  setMotionPreviewsCamera: (camera: string | null) => void;
  setMotionSearchCamera: (camera: string) => void;
  emptyCardData: EmptyCardData;
  onOpenRecording: (data: RecordingStartingPoint) => void;
};
function MotionReview({
  contentRef,
  reviewItems,
  relevantPreviews,
  reviewSummary,
  recordingsSummary,
  timeRange,
  startTime,
  filter,
  motionOnly = false,
  updateFilter,
  motionPreviewsCamera,
  setMotionPreviewsCamera,
  setMotionSearchCamera,
  emptyCardData,
  onOpenRecording,
}: MotionReviewProps) {
  const { t } = useTranslation(["views/events", "common"]);
  const segmentDuration = 30;
  const { data: config } = useSWR<FrigateConfig>("config");
  const allowedCameras = useAllowedCameras();

  const reviewCameras = useMemo(() => {
    if (!config) {
      return [];
    }

    const selectedCams = filter?.cameras;
    const cameras = Object.values(config.cameras).filter((cam) => {
      if (isReplayCamera(cam.name)) {
        return false;
      }
      if (!allowedCameras.includes(cam.name)) {
        return false;
      }
      if (selectedCams && !selectedCams.includes(cam.name)) {
        return false;
      }
      return true;
    });

    return cameras.sort((a, b) => a.ui.order - b.ui.order);
  }, [config, filter, allowedCameras]);

  const videoPlayersRef = useRef<{ [camera: string]: PreviewController }>({});

  // motion data

  const { alignStartDateToTimeline, alignEndDateToTimeline } = useTimelineUtils(
    {
      segmentDuration,
    },
  );

  const alignedAfter = alignStartDateToTimeline(timeRange.after);
  const alignedBefore = alignEndDateToTimeline(timeRange.before);

  const { data: motionData } = useSWR<MotionData[]>([
    "review/activity/motion",
    {
      before: alignedBefore,
      after: alignedAfter,
      scale: segmentDuration / 2,
      cameras: filter?.cameras?.join(",") ?? null,
    },
  ]);

  const { data: overlapReviewSegments } = useSWR<ReviewSegment[]>([
    "review",
    {
      before: alignedBefore,
      after: alignedAfter,
      cameras: filter?.cameras?.join(",") ?? null,
    },
  ]);

  // timeline time

  const timeRangeSegments = useMemo(
    () => getChunkedTimeRange(timeRange.after, timeRange.before),
    [timeRange],
  );

  const initialIndex = useMemo(() => {
    if (!startTime) {
      return timeRangeSegments.ranges.length - 1;
    }

    const index = timeRangeSegments.ranges.findIndex(
      (seg) => seg.after <= startTime && seg.before >= startTime,
    );

    if (index === -1) {
      return timeRangeSegments.ranges.length - 1;
    }

    return index;
    // only render once
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  const [selectedRangeIdx, setSelectedRangeIdx] = useState(initialIndex);
  const [currentTime, setCurrentTime] = useState<number>(
    startTime ??
      timeRangeSegments.ranges[selectedRangeIdx]?.before ??
      timeRangeSegments.end,
  );
  const currentTimeRange = useMemo(
    () =>
      timeRangeSegments.ranges[selectedRangeIdx] ??
      timeRangeSegments.ranges[timeRangeSegments.ranges.length - 1],
    [selectedRangeIdx, timeRangeSegments],
  );

  const [previewStart, setPreviewStart] = useState(startTime);

  const [scrubbing, setScrubbing] = useState(false);
  const [playing, setPlaying] = useState(false);

  // move to next clip

  useEffect(() => {
    if (
      currentTime > currentTimeRange.before + 60 ||
      currentTime < currentTimeRange.after - 60
    ) {
      const index = timeRangeSegments.ranges.findIndex(
        (seg) => seg.after <= currentTime && seg.before >= currentTime,
      );

      if (index != -1) {
        setPreviewStart(currentTime);
        setSelectedRangeIdx(index);
      }
      return;
    }

    Object.values(videoPlayersRef.current).forEach((controller) => {
      controller.scrubToTimestamp(currentTime);
    });
    // only refresh when current time or available segments changes
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [currentTime, timeRangeSegments]);

  // playback

  const [playbackRate, setPlaybackRate] = useState(8);
  const [controlsOpen, setControlsOpen] = useState(false);
  const [dimStrength, setDimStrength] = useState(82);
  const [isPreviewSettingsOpen, setIsPreviewSettingsOpen] = useState(false);
  const [motionFilterCells, setMotionFilterCells] = useState<Set<number>>(
    new Set(),
  );
  const [pendingFilterCells, setPendingFilterCells] = useState<Set<number>>(
    new Set(),
  );
  const [isRegionFilterOpen, setIsRegionFilterOpen] = useState(false);

  // reset filter when camera changes
  useEffect(() => {
    setMotionFilterCells(new Set());
    setPendingFilterCells(new Set());
  }, [motionPreviewsCamera]);

  const objectReviewItems = useMemo(
    () =>
      (overlapReviewSegments ?? []).filter(
        (item) =>
          item.severity === "alert" ||
          item.severity === "detection" ||
          (item.data.detections?.length ?? 0) > 0 ||
          (item.data.objects?.length ?? 0) > 0,
      ),
    [overlapReviewSegments],
  );

  const nextTimestamp = useCameraMotionNextTimestamp(
    timeRangeSegments.end,
    segmentDuration,
    motionOnly,
    objectReviewItems,
    motionData ?? [],
    currentTime,
  );

  const timeoutIdRef = useRef<NodeJS.Timeout | null>(null);

  const selectedMotionPreviewCamera = useMemo(
    () =>
      reviewCameras.find((camera) => camera.name === motionPreviewsCamera) ??
      null,
    [motionPreviewsCamera, reviewCameras],
  );

  const onUpdateSelectedDay = useCallback(
    (day?: Date) => {
      updateFilter({
        ...filter,
        after: day == undefined ? undefined : day.getTime() / 1000,
        before: day == undefined ? undefined : getEndOfDayTimestamp(day),
      });
    },
    [filter, updateFilter],
  );

  const selectedCameraMotionData = useMemo(() => {
    if (!motionPreviewsCamera) {
      return [];
    }

    return (motionData ?? []).filter((item) => {
      const cameras = item.camera.split(",").map((camera) => camera.trim());
      return cameras.includes(motionPreviewsCamera);
    });
  }, [motionData, motionPreviewsCamera]);

  const selectedCameraReviewItems = useMemo(() => {
    if (!motionPreviewsCamera) {
      return [];
    }

    return objectReviewItems.filter(
      (item) => item.camera === motionPreviewsCamera,
    );
  }, [motionPreviewsCamera, objectReviewItems]);

  const motionPreviewRanges = useCameraMotionOnlyRanges(
    segmentDuration,
    selectedCameraReviewItems,
    selectedCameraMotionData,
  );

  useEffect(() => {
    if (
      motionPreviewsCamera &&
      !reviewCameras.some((camera) => camera.name === motionPreviewsCamera)
    ) {
      setMotionPreviewsCamera(null);
    }
  }, [motionPreviewsCamera, reviewCameras, setMotionPreviewsCamera]);

  useEffect(() => {
    if (nextTimestamp) {
      if (!playing && timeoutIdRef.current != null) {
        clearTimeout(timeoutIdRef.current);
        return;
      }

      if (nextTimestamp >= timeRange.before - 4) {
        setPlaying(false);
        return;
      }

      const handleTimeout = () => {
        setCurrentTime(nextTimestamp);
        timeoutIdRef.current = setTimeout(handleTimeout, 500 / playbackRate);
      };

      timeoutIdRef.current = setTimeout(handleTimeout, 500 / playbackRate);

      return () => {
        if (timeoutIdRef.current) {
          clearTimeout(timeoutIdRef.current);
        }
      };
    }
  }, [playing, playbackRate, nextTimestamp, setPlaying, timeRange]);

  const getDetectionType = useCallback(
    (cameraName: string) => {
      if (motionOnly) {
        const segmentStartTime = alignStartDateToTimeline(currentTime);
        const segmentEndTime = segmentStartTime + segmentDuration;
        const matchingItem = motionData?.find((item) => {
          const cameras = item.camera.split(",").map((camera) => camera.trim());
          return (
            item.start_time >= segmentStartTime &&
            item.start_time < segmentEndTime &&
            cameras.includes(cameraName)
          );
        });

        return matchingItem ? "significant_motion" : null;
      } else {
        const segmentStartTime = alignStartDateToTimeline(currentTime);
        const segmentEndTime = segmentStartTime + segmentDuration;
        const matchingItem = reviewItems?.all.find((item) => {
          const endTime = item.end_time ?? timeRange.before;

          return (
            ((item.start_time >= segmentStartTime &&
              item.start_time < segmentEndTime) ||
              (endTime > segmentStartTime && endTime <= segmentEndTime) ||
              (item.start_time <= segmentStartTime &&
                endTime >= segmentEndTime)) &&
            item.camera === cameraName
          );
        });

        return matchingItem ? matchingItem.severity : null;
      }
    },
    [
      reviewItems,
      motionData,
      currentTime,
      timeRange,
      motionOnly,
      alignStartDateToTimeline,
    ],
  );

  if (motionData?.length === 0) {
    return (
      <div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2">
        <EmptyCard
          title={emptyCardData.title}
          description={emptyCardData.description}
          icon={<LuFolderX className="size-16" />}
        />
      </div>
    );
  }

  if (relevantPreviews == undefined) {
    return <ActivityIndicator />;
  }

  return (
    <>
      {selectedMotionPreviewCamera && (
        <>
          <div className="relative mb-2 flex h-11 items-center justify-between pl-2 pr-2 md:px-3">
            <Button
              className="flex items-center gap-2.5 rounded-lg"
              aria-label={t("label.back", { ns: "common" })}
              size="sm"
              onClick={() => setMotionPreviewsCamera(null)}
            >
              <IoMdArrowRoundBack className="size-5 text-secondary-foreground" />
              {isDesktop && (
                <div className="text-primary">
                  {t("button.back", { ns: "common" })}
                </div>
              )}
            </Button>

            <div className="flex items-center gap-2">
              {isDesktop && (
                <CalendarFilterButton
                  reviewSummary={reviewSummary}
                  recordingsSummary={recordingsSummary}
                  day={
                    filter?.after == undefined
                      ? undefined
                      : new Date(filter.after * 1000)
                  }
                  updateSelectedDay={onUpdateSelectedDay}
                />
              )}
              <Dialog
                open={isRegionFilterOpen}
                onOpenChange={(open) => {
                  if (open) {
                    setPendingFilterCells(new Set(motionFilterCells));
                  }
                  setIsRegionFilterOpen(open);
                }}
              >
                <DialogTrigger asChild>
                  <Button
                    className={cn(
                      isDesktop ? "flex items-center gap-2" : "rounded-lg",
                    )}
                    size="sm"
                    variant={motionFilterCells.size > 0 ? "select" : "default"}
                    aria-label={t("motionPreviews.filter")}
                  >
                    <FaFilter
                      className={
                        motionFilterCells.size > 0
                          ? "text-selected-foreground"
                          : "text-secondary-foreground"
                      }
                    />
                    {isDesktop && t("motionPreviews.filter")}
                  </Button>
                </DialogTrigger>
                <DialogContent className="max-h-[90dvh] overflow-y-auto sm:max-w-[85%] md:max-w-[70%] lg:max-w-[60%]">
                  <DialogHeader>
                    <DialogTitle>{t("motionPreviews.filter")}</DialogTitle>
                    <DialogDescription>
                      {t("motionPreviews.filterDesc")}
                    </DialogDescription>
                  </DialogHeader>
                  <MotionRegionFilterGrid
                    cameraName={selectedMotionPreviewCamera.name}
                    selectedCells={pendingFilterCells}
                    onCellsChange={setPendingFilterCells}
                  />
                  <DialogFooter>
                    <Button
                      disabled={pendingFilterCells.size === 0}
                      onClick={() => {
                        setPendingFilterCells(new Set());
                      }}
                    >
                      {t("motionPreviews.filterClear")}
                    </Button>
                    <Button
                      variant="select"
                      onClick={() => {
                        setMotionFilterCells(new Set(pendingFilterCells));
                        setIsRegionFilterOpen(false);
                      }}
                    >
                      {t("button.apply", { ns: "common" })}
                    </Button>
                  </DialogFooter>
                </DialogContent>
              </Dialog>
              <PlatformAwareDialog
                trigger={
                  <Button
                    className={cn(
                      isDesktop ? "flex items-center gap-2" : "rounded-lg",
                    )}
                    size="sm"
                    aria-label={
                      isDesktop
                        ? t("motionPreviews.mobileSettingsTitle")
                        : t("filters", { ns: "views/recording" })
                    }
                  >
                    <FaCog className="text-secondary-foreground" />
                    {isDesktop && t("motionPreviews.mobileSettingsTitle")}
                  </Button>
                }
                content={
                  <div className="space-y-4 py-2">
                    {!isDesktop && (
                      <div className="space-y-1">
                        <div>{t("motionPreviews.mobileSettingsTitle")}</div>
                        <div className="text-xs text-muted-foreground">
                          {t("motionPreviews.mobileSettingsDesc")}
                        </div>
                      </div>
                    )}

                    <div className="space-y-3">
                      <div className="space-y-0.5">
                        <div>{t("motionPreviews.speed")}</div>
                        <div className="text-xs text-muted-foreground">
                          {t("motionPreviews.speedDesc")}
                        </div>
                      </div>
                      <Select
                        value={String(playbackRate)}
                        onValueChange={(value) =>
                          setPlaybackRate(Number(value))
                        }
                      >
                        <SelectTrigger
                          className="h-10 w-full"
                          aria-label={t("motionPreviews.speedAria")}
                        >
                          <SelectValue />
                        </SelectTrigger>
                        <SelectContent>
                          {[4, 8, 12, 16].map((speed) => (
                            <SelectItem key={speed} value={String(speed)}>
                              {speed}x
                            </SelectItem>
                          ))}
                        </SelectContent>
                      </Select>
                    </div>

                    <div className="space-y-3">
                      <div className="space-y-0.5">
                        <div>{t("motionPreviews.dim")}</div>
                        <div className="text-xs text-muted-foreground">
                          {t("motionPreviews.dimDesc")}
                        </div>
                      </div>
                      <div className="px-1 py-2">
                        <VolumeSlider
                          className="w-full"
                          min={25}
                          max={95}
                          step={1}
                          value={[dimStrength]}
                          aria-label={t("motionPreviews.dimAria")}
                          onValueChange={(values) => {
                            const nextValue = values[0];
                            if (nextValue == undefined) {
                              return;
                            }

                            setDimStrength(nextValue);
                          }}
                        />
                      </div>
                    </div>

                    {!isDesktop && (
                      <>
                        <SelectSeparator />

                        <div className="flex w-full flex-row justify-center">
                          <ReviewActivityCalendar
                            recordingsSummary={recordingsSummary}
                            selectedDay={
                              filter?.after == undefined
                                ? undefined
                                : new Date(filter.after * 1000)
                            }
                            onSelect={(day) => {
                              onUpdateSelectedDay(day);
                              setIsPreviewSettingsOpen(false);
                            }}
                          />
                        </div>
                        <div className="flex items-center justify-center p-2">
                          <Button
                            aria-label={t("button.reset", { ns: "common" })}
                            onClick={() => {
                              onUpdateSelectedDay(undefined);
                              setIsPreviewSettingsOpen(false);
                            }}
                          >
                            {t("button.reset", { ns: "common" })}
                          </Button>
                        </div>
                      </>
                    )}
                  </div>
                }
                contentClassName={cn(
                  isDesktop
                    ? "w-80"
                    : "scrollbar-container max-h-[75dvh] overflow-y-auto overflow-x-hidden px-4",
                )}
                open={isPreviewSettingsOpen}
                onOpenChange={setIsPreviewSettingsOpen}
              />
            </div>
          </div>

          <MotionPreviewsPane
            camera={selectedMotionPreviewCamera}
            contentRef={contentRef}
            cameraPreviews={relevantPreviews}
            motionRanges={motionPreviewRanges}
            isLoadingMotionRanges={
              motionData == undefined || overlapReviewSegments == undefined
            }
            playbackRate={playbackRate}
            nonMotionAlpha={dimStrength / 100}
            motionFilterCells={motionFilterCells}
            onSeek={(timestamp) => {
              onOpenRecording({
                camera: selectedMotionPreviewCamera.name,
                startTime: timestamp,
                severity: "significant_motion",
              });
            }}
          />
        </>
      )}
      <div
        className={cn(
          "no-scrollbar flex min-w-0 flex-1 flex-wrap content-start gap-2 overflow-y-auto md:gap-4",
          selectedMotionPreviewCamera && "hidden",
        )}
      >
        <div
          ref={selectedMotionPreviewCamera ? undefined : contentRef}
          className={cn(
            "no-scrollbar grid w-full grid-cols-1",
            isMobile && "landscape:grid-cols-2",
            reviewCameras.length > 3 &&
              isMobile &&
              "portrait:md:grid-cols-2 landscape:md:grid-cols-3",
            isDesktop && "grid-cols-2 lg:grid-cols-3",
            "gap-2 overflow-auto px-1 md:mx-2 md:gap-4 xl:grid-cols-3 3xl:grid-cols-4",
          )}
        >
          {reviewCameras.map((camera) => {
            let grow;
            let spans;
            const aspectRatio = camera.detect.width / camera.detect.height;
            if (aspectRatio > 2) {
              grow = "aspect-wide";
              spans = "sm:col-span-2";
            } else if (aspectRatio < 1) {
              grow = "h-full aspect-tall";
              spans = "md:row-span-2";
            } else {
              grow = "aspect-video";
            }
            const detectionType = getDetectionType(camera.name);
            return (
              <div key={camera.name} className={`relative ${spans}`}>
                {motionData ? (
                  <>
                    <PreviewPlayer
                      className={`rounded-lg md:rounded-2xl ${spans} ${grow}`}
                      camera={camera.name}
                      timeRange={currentTimeRange}
                      startTime={previewStart}
                      cameraPreviews={relevantPreviews}
                      isScrubbing={scrubbing}
                      onControllerReady={(controller) => {
                        videoPlayersRef.current[camera.name] = controller;
                      }}
                      onClick={() =>
                        onOpenRecording({
                          camera: camera.name,
                          startTime: Math.min(
                            currentTime,
                            Date.now() / 1000 - 30,
                          ),
                          severity: "significant_motion",
                        })
                      }
                    />
                    <div
                      className={`review-item-ring pointer-events-none absolute inset-0 z-20 size-full rounded-lg outline outline-[3px] -outline-offset-[2.8px] ${detectionType ? `outline-severity_${detectionType} shadow-severity_${detectionType}` : "outline-transparent duration-500"}`}
                    />
                    <div className="absolute bottom-2 right-2 z-30">
                      <DropdownMenu>
                        <DropdownMenuTrigger asChild>
                          <BlurredIconButton
                            aria-label={t("motionSearch.openMenu")}
                            onClick={(e) => e.stopPropagation()}
                          >
                            <FiMoreVertical className="size-5" />
                          </BlurredIconButton>
                        </DropdownMenuTrigger>
                        <DropdownMenuContent align="end">
                          <DropdownMenuItem
                            onClick={(e) => {
                              e.stopPropagation();
                              setMotionPreviewsCamera(camera.name);
                            }}
                          >
                            {t("motionPreviews.menuItem")}
                          </DropdownMenuItem>
                          <DropdownMenuItem
                            onClick={(e) => {
                              e.stopPropagation();
                              setMotionSearchCamera(camera.name);
                            }}
                          >
                            {t("motionSearch.menuItem")}
                          </DropdownMenuItem>
                        </DropdownMenuContent>
                      </DropdownMenu>
                    </div>
                  </>
                ) : (
                  <Skeleton
                    className={`size-full rounded-lg md:rounded-2xl ${spans} ${grow}`}
                  />
                )}
              </div>
            );
          })}
        </div>
      </div>
      {!selectedMotionPreviewCamera && (
        <div className="no-scrollbar w-[55px] overflow-y-auto md:w-[100px]">
          {motionData ? (
            <MotionReviewTimeline
              segmentDuration={segmentDuration}
              timestampSpread={15}
              timelineStart={timeRangeSegments.end}
              timelineEnd={timeRangeSegments.start}
              motionOnly={motionOnly}
              showHandlebar
              handlebarTime={currentTime}
              setHandlebarTime={setCurrentTime}
              events={reviewItems?.all ?? []}
              motion_events={motionData ?? []}
              contentRef={contentRef}
              onHandlebarDraggingChange={(scrubbing) => {
                if (playing && scrubbing) {
                  setPlaying(false);
                }

                setScrubbing(scrubbing);
              }}
              dense={isMobileOnly}
              isZooming={false}
              zoomDirection={null}
              alwaysShowMotionLine={true}
            />
          ) : (
            <Skeleton className="size-full" />
          )}
        </div>
      )}

      {!selectedMotionPreviewCamera && (
        <VideoControls
          className="absolute bottom-16 left-1/2 -translate-x-1/2 bg-secondary"
          features={{
            volume: false,
            seek: true,
            playbackRate: true,
            fullscreen: false,
          }}
          isPlaying={playing}
          show={!scrubbing || controlsOpen}
          playbackRates={[4, 8, 12, 16]}
          playbackRate={playbackRate}
          setControlsOpen={setControlsOpen}
          onPlayPause={setPlaying}
          onSeek={(diff) => {
            const wasPlaying = playing;

            if (wasPlaying) {
              setPlaying(false);
            }

            setCurrentTime(currentTime + diff);

            if (wasPlaying) {
              setTimeout(() => setPlaying(true), 100);
            }
          }}
          onSetPlaybackRate={setPlaybackRate}
        />
      )}
    </>
  );
}
