import Heading from "../ui/heading";
import { Separator } from "../ui/separator";
import { Button } from "@/components/ui/button";
import {
  Form,
  FormControl,
  FormDescription,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { useCallback, useEffect, useMemo, useState } from "react";
import { FrigateConfig } from "@/types/frigateConfig";
import useSWR from "swr";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { ZoneFormValuesType, Polygon } from "@/types/canvas";
import { reviewQueries } from "@/utils/zoneEdutUtil";
import { Switch } from "../ui/switch";
import { Label } from "../ui/label";
import PolygonEditControls from "./PolygonEditControls";
import { FaCheckCircle } from "react-icons/fa";
import axios from "axios";
import { toast } from "sonner";
import { flattenPoints, interpolatePoints } from "@/utils/canvasUtil";
import ActivityIndicator from "../indicators/activity-indicator";
import { getAttributeLabels } from "@/utils/iconUtil";
import { Trans, useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import { LuExternalLink } from "react-icons/lu";
import { useDocDomain } from "@/hooks/use-doc-domain";
import { getTranslatedLabel } from "@/utils/i18n";
import NameAndIdFields from "../input/NameAndIdFields";
import { useZoneState } from "@/api/ws";

type ZoneEditPaneProps = {
  polygons?: Polygon[];
  setPolygons: React.Dispatch<React.SetStateAction<Polygon[]>>;
  activePolygonIndex?: number;
  scaledWidth?: number;
  scaledHeight?: number;
  isLoading: boolean;
  setIsLoading: React.Dispatch<React.SetStateAction<boolean>>;
  onSave?: () => void;
  onCancel?: () => void;
  setActiveLine: React.Dispatch<React.SetStateAction<number | undefined>>;
  snapPoints: boolean;
  setSnapPoints: React.Dispatch<React.SetStateAction<boolean>>;
  editingProfile?: string | null;
};

export default function ZoneEditPane({
  polygons,
  setPolygons,
  activePolygonIndex,
  scaledWidth,
  scaledHeight,
  isLoading,
  setIsLoading,
  onSave,
  onCancel,
  setActiveLine,
  snapPoints,
  setSnapPoints,
  editingProfile,
}: ZoneEditPaneProps) {
  const { t } = useTranslation(["views/settings"]);
  const { getLocaleDocUrl } = useDocDomain();
  const { data: config, mutate: updateConfig } =
    useSWR<FrigateConfig>("config");

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

    return Object.values(config.cameras)
      .filter((conf) => conf.ui.dashboard && conf.enabled_in_config)
      .sort((aConf, bConf) => aConf.ui.order - bConf.ui.order);
  }, [config]);

  const polygon = useMemo(() => {
    if (polygons && activePolygonIndex !== undefined) {
      return polygons[activePolygonIndex];
    } else {
      return null;
    }
  }, [polygons, activePolygonIndex]);

  const zoneName = polygon?.name || "";
  const { send: sendZoneState } = useZoneState(polygon?.camera || "", zoneName);

  const isExistingZone = !!polygon && polygon.name.length > 0;

  const idDisabled = useMemo(() => {
    if (!isExistingZone || !polygon) {
      return false;
    }
    if (editingProfile) {
      return true;
    }
    const cam = config?.cameras[polygon.camera];
    if (!cam) {
      return false;
    }
    const inRequiredZones =
      cam.review.alerts.required_zones.includes(polygon.name) ||
      cam.review.detections.required_zones.includes(polygon.name);
    const hasProfileOverride = Object.values(cam.profiles ?? {}).some(
      (profile) => profile?.zones && polygon.name in profile.zones,
    );
    return inRequiredZones || hasProfileOverride;
  }, [config, polygon, editingProfile, isExistingZone]);

  const cameraConfig = useMemo(() => {
    if (polygon?.camera && config) {
      return config.cameras[polygon.camera];
    }
  }, [polygon, config]);

  const [lineA, lineB, lineC, lineD] = useMemo(() => {
    if (!polygon?.camera || !polygon?.name || !config) {
      return [undefined, undefined, undefined, undefined];
    }

    // Check profile zone first, then base
    const profileZone = editingProfile
      ? config.cameras[polygon.camera]?.profiles?.[editingProfile]?.zones?.[
          polygon.name
        ]
      : undefined;
    const baseZone = config.cameras[polygon.camera]?.zones[polygon.name];
    const distances = profileZone?.distances ?? baseZone?.distances;

    return Array.isArray(distances)
      ? distances.map((value) => parseFloat(value) || 0)
      : [undefined, undefined, undefined, undefined];
  }, [polygon, config, editingProfile]);

  const formSchema = z
    .object({
      name: z
        .string()
        .min(2, {
          message: t(
            "masksAndZones.form.zoneName.error.mustBeAtLeastTwoCharacters",
          ),
        })
        .transform((val: string) => val.trim().replace(/\s+/g, "_"))
        .refine(
          (value: string) => {
            return !cameras.map((cam) => cam.name).includes(value);
          },
          {
            message: t(
              "masksAndZones.form.zoneName.error.mustNotBeSameWithCamera",
            ),
          },
        )
        .refine(
          (value: string) => {
            const otherPolygonNames =
              polygons
                ?.filter((_, index) => index !== activePolygonIndex)
                .map((polygon) => polygon.name) || [];

            return !otherPolygonNames.includes(value);
          },
          {
            message: t("masksAndZones.form.zoneName.error.alreadyExists"),
          },
        )
        .refine(
          (value: string) => {
            return !value.includes(".");
          },
          {
            message: t(
              "masksAndZones.form.zoneName.error.mustNotContainPeriod",
            ),
          },
        ),
      friendly_name: z
        .string()
        .min(2, {
          message: t(
            "masksAndZones.form.zoneName.error.mustBeAtLeastTwoCharacters",
          ),
        })
        .refine(
          (value: string) => {
            return !cameras.map((cam) => cam.name).includes(value);
          },
          {
            message: t(
              "masksAndZones.form.zoneName.error.mustNotBeSameWithCamera",
            ),
          },
        )
        .refine(
          (value: string) => {
            const otherPolygonNames =
              polygons
                ?.filter((_, index) => index !== activePolygonIndex)
                .map((polygon) => polygon.name) || [];

            return !otherPolygonNames.includes(value);
          },
          {
            message: t("masksAndZones.form.zoneName.error.alreadyExists"),
          },
        ),
      enabled: z.boolean().default(true),
      inertia: z.coerce
        .number()
        .min(1, {
          message: t("masksAndZones.form.inertia.error.mustBeAboveZero"),
        })
        .or(z.literal("")),
      loitering_time: z.coerce
        .number()
        .min(0, {
          message: t(
            "masksAndZones.form.loiteringTime.error.mustBeGreaterOrEqualZero",
          ),
        })
        .optional()
        .or(z.literal("")),
      isFinished: z.boolean().refine((val) => val === true, {
        message: t("masksAndZones.form.polygonDrawing.error.mustBeFinished"),
      }),
      objects: z.array(z.string()).optional(),
      review_alerts: z.boolean().default(false).optional(),
      review_detections: z.boolean().default(false).optional(),
      speedEstimation: z.boolean().default(false),
      lineA: z.coerce
        .number()
        .min(0.1, {
          message: t("masksAndZones.form.distance.error.text"),
        })
        .optional()
        .or(z.literal("")),
      lineB: z.coerce
        .number()
        .min(0.1, {
          message: t("masksAndZones.form.distance.error.text"),
        })
        .optional()
        .or(z.literal("")),
      lineC: z.coerce
        .number()
        .min(0.1, {
          message: t("masksAndZones.form.distance.error.text"),
        })
        .optional()
        .or(z.literal("")),
      lineD: z.coerce
        .number()
        .min(0.1, {
          message: t("masksAndZones.form.distance.error.text"),
        })
        .optional()
        .or(z.literal("")),
      speed_threshold: z.coerce
        .number()
        .min(0.1, {
          message: t("masksAndZones.form.speed.error.mustBeGreaterOrEqualTo"),
        })
        .optional()
        .or(z.literal("")),
    })
    .refine(
      (data) => {
        if (data.speedEstimation) {
          return !!data.lineA && !!data.lineB && !!data.lineC && !!data.lineD;
        }
        return true;
      },
      {
        message: t("masksAndZones.form.distance.error.mustBeFilled"),
        path: ["speedEstimation"],
      },
    )
    .refine(
      (data) => {
        // Prevent speed estimation when loitering_time is greater than 0
        return !(
          data.speedEstimation &&
          data.loitering_time &&
          data.loitering_time > 0
        );
      },
      {
        message: t(
          "masksAndZones.zones.speedThreshold.toast.error.loiteringTimeError",
        ),
        path: ["loitering_time"],
      },
    );

  // Resolve zone data: profile zone takes priority over base
  const resolvedZoneData = useMemo(() => {
    if (!polygon?.camera || !polygon?.name || !config) return undefined;
    const cam = config.cameras[polygon.camera];
    if (!cam) return undefined;
    const profileZone = editingProfile
      ? cam.profiles?.[editingProfile]?.zones?.[polygon.name]
      : undefined;
    return profileZone ?? cam.zones[polygon.name];
  }, [polygon, config, editingProfile]);

  const form = useForm<z.infer<typeof formSchema>>({
    resolver: zodResolver(formSchema),
    mode: "onChange",
    defaultValues: {
      name: polygon?.name ?? "",
      friendly_name: polygon?.friendly_name ?? polygon?.name ?? "",
      enabled:
        resolvedZoneData?.enabled !== undefined
          ? resolvedZoneData.enabled
          : (polygon?.enabled ?? true),
      inertia: resolvedZoneData?.inertia ?? 3,
      loitering_time: resolvedZoneData?.loitering_time ?? 0,
      isFinished: polygon?.isFinished ?? false,
      objects: polygon?.objects ?? [],
      speedEstimation: !!(lineA || lineB || lineC || lineD),
      lineA,
      lineB,
      lineC,
      lineD,
      speed_threshold: resolvedZoneData?.speed_threshold,
    },
  });

  const watchSpeedEstimation = form.watch("speedEstimation");
  const watchLineA = form.watch("lineA");
  const watchLineB = form.watch("lineB");
  const watchLineC = form.watch("lineC");
  const watchLineD = form.watch("lineD");

  const canSave =
    form.formState.isValid &&
    (!watchSpeedEstimation ||
      (!!watchLineA && !!watchLineB && !!watchLineC && !!watchLineD));

  useEffect(() => {
    if (watchSpeedEstimation && polygon && polygon.points.length !== 4) {
      toast.error(
        t("masksAndZones.zones.speedThreshold.toast.error.pointLengthError"),
      );
      form.setValue("speedEstimation", false);
    }
  }, [polygon, form, t, watchSpeedEstimation]);

  useEffect(() => {
    if (polygon?.isFinished !== undefined) {
      form.setValue("isFinished", polygon.isFinished, { shouldValidate: true });
    }
  }, [polygon?.isFinished, form]);

  const saveToConfig = useCallback(
    async (
      {
        name: zoneName,
        friendly_name,
        enabled,
        inertia,
        loitering_time,
        objects: form_objects,
        speedEstimation,
        lineA,
        lineB,
        lineC,
        lineD,
        speed_threshold,
      }: ZoneFormValuesType, // values submitted via the form
      objects: string[],
    ) => {
      if (!scaledWidth || !scaledHeight || !polygon) {
        return;
      }

      // Determine config path prefix based on profile mode
      const pathPrefix = editingProfile
        ? `cameras.${polygon.camera}.profiles.${editingProfile}.zones.${zoneName}`
        : `cameras.${polygon.camera}.zones.${zoneName}`;

      const oldPathPrefix = editingProfile
        ? `cameras.${polygon.camera}.profiles.${editingProfile}.zones.${polygon.name}`
        : `cameras.${polygon.camera}.zones.${polygon.name}`;

      let mutatedConfig = config;
      let alertQueries = "";
      let detectionQueries = "";

      const renamingZone = zoneName != polygon.name && polygon.name != "";

      if (renamingZone) {
        // rename - delete old zone and replace with new
        let renameAlertQueries = "";
        let renameDetectionQueries = "";

        // Only handle review queries for base config (not profiles)
        if (!editingProfile) {
          const zoneInAlerts =
            cameraConfig?.review.alerts.required_zones.includes(polygon.name) ??
            false;
          const zoneInDetections =
            cameraConfig?.review.detections.required_zones.includes(
              polygon.name,
            ) ?? false;

          ({
            alertQueries: renameAlertQueries,
            detectionQueries: renameDetectionQueries,
          } = reviewQueries(
            polygon.name,
            false,
            false,
            polygon.camera,
            cameraConfig?.review.alerts.required_zones || [],
            cameraConfig?.review.detections.required_zones || [],
          ));

          try {
            await axios.put(
              `config/set?${oldPathPrefix}${renameAlertQueries}${renameDetectionQueries}`,
              {
                requires_restart: 0,
                update_topic: `config/cameras/${polygon.camera}/zones`,
              },
            );

            // Wait for the config to be updated
            mutatedConfig = await updateConfig();
          } catch {
            toast.error(t("toast.save.error.noMessage", { ns: "common" }), {
              position: "top-center",
            });
            setIsLoading(false);
            return;
          }

          // make sure new zone name is readded to review
          ({ alertQueries, detectionQueries } = reviewQueries(
            zoneName,
            zoneInAlerts,
            zoneInDetections,
            polygon.camera,
            mutatedConfig?.cameras[polygon.camera]?.review.alerts
              .required_zones || [],
            mutatedConfig?.cameras[polygon.camera]?.review.detections
              .required_zones || [],
          ));
        } else {
          // Profile mode: just delete the old profile zone path
          try {
            await axios.put(`config/set?${oldPathPrefix}`, {
              requires_restart: 0,
            });
            mutatedConfig = await updateConfig();
          } catch {
            toast.error(t("toast.save.error.noMessage", { ns: "common" }), {
              position: "top-center",
            });
            setIsLoading(false);
            return;
          }
        }
      }

      const coordinates = flattenPoints(
        interpolatePoints(polygon.points, scaledWidth, scaledHeight, 1, 1),
      ).join(",");

      let objectQueries = objects
        .map((object) => `&${pathPrefix}.objects=${object}`)
        .join("");

      const same_objects =
        form_objects.length == objects.length &&
        form_objects.every(function (element, index) {
          return element === objects[index];
        });

      // deleting objects
      if (!objectQueries && !same_objects && !renamingZone) {
        objectQueries = `&${pathPrefix}.objects`;
      }

      let inertiaQuery = "";
      if (inertia) {
        inertiaQuery = `&${pathPrefix}.inertia=${inertia}`;
      }

      let loiteringTimeQuery = "";
      if (loitering_time >= 0) {
        loiteringTimeQuery = `&${pathPrefix}.loitering_time=${loitering_time}`;
      }

      let distancesQuery = "";
      const distances = [lineA, lineB, lineC, lineD].filter(Boolean).join(",");
      if (speedEstimation) {
        distancesQuery = `&${pathPrefix}.distances=${distances}`;
      } else {
        if (distances != "") {
          distancesQuery = `&${pathPrefix}.distances`;
        }
      }

      let speedThresholdQuery = "";
      if (speed_threshold >= 0 && speedEstimation) {
        speedThresholdQuery = `&${pathPrefix}.speed_threshold=${speed_threshold}`;
      } else {
        if (resolvedZoneData?.speed_threshold) {
          speedThresholdQuery = `&${pathPrefix}.speed_threshold`;
        }
      }

      let friendlyNameQuery = "";
      if (friendly_name && friendly_name !== zoneName) {
        friendlyNameQuery = `&${pathPrefix}.friendly_name=${encodeURIComponent(friendly_name)}`;
      }

      const enabledQuery = `&${pathPrefix}.enabled=${enabled ? "True" : "False"}`;

      const updateTopic = editingProfile
        ? undefined
        : `config/cameras/${polygon.camera}/zones`;

      axios
        .put(
          `config/set?${pathPrefix}.coordinates=${coordinates}${enabledQuery}${inertiaQuery}${loiteringTimeQuery}${speedThresholdQuery}${distancesQuery}${objectQueries}${friendlyNameQuery}${alertQueries}${detectionQueries}`,
          {
            requires_restart: 0,
            update_topic: updateTopic,
          },
        )
        .then((res) => {
          if (res.status === 200) {
            toast.success(
              t("masksAndZones.zones.toast.success", {
                zoneName: friendly_name || zoneName,
              }),
              {
                position: "top-center",
              },
            );
            updateConfig();
            // Only publish WS state for base config when zone has a name and
            // wasn't renamed (the hook is bound to the old name).
            if (!editingProfile && polygon?.name && !renamingZone) {
              sendZoneState(enabled ? "ON" : "OFF");
            }
          } else {
            toast.error(
              t("toast.save.error.title", {
                errorMessage: res.statusText,
                ns: "common",
              }),
              {
                position: "top-center",
              },
            );
          }
        })
        .catch((error) => {
          const errorMessage =
            error.response?.data?.message ||
            error.response?.data?.detail ||
            "Unknown error";
          toast.error(
            t("toast.save.error.title", {
              errorMessage,
              ns: "common",
            }),
            {
              position: "top-center",
            },
          );
        })
        .finally(() => {
          setIsLoading(false);
        });
    },
    [
      config,
      updateConfig,
      polygon,
      scaledWidth,
      scaledHeight,
      setIsLoading,
      cameraConfig,
      t,
      sendZoneState,
      editingProfile,
      resolvedZoneData,
    ],
  );

  function onSubmit(values: z.infer<typeof formSchema>) {
    if (activePolygonIndex === undefined || !values || !polygons) {
      return;
    }
    setIsLoading(true);

    saveToConfig(
      values as ZoneFormValuesType,
      polygons[activePolygonIndex].objects,
    );

    if (onSave) {
      onSave();
    }
  }

  useEffect(() => {
    document.title = t("masksAndZones.zones.documentTitle");
  }, [t]);

  if (!polygon) {
    return;
  }

  return (
    <>
      <Heading as="h3" className="my-2">
        {polygon.name.length
          ? t("masksAndZones.zones.edit")
          : t("masksAndZones.zones.add")}
      </Heading>
      <div className="my-2 text-sm text-muted-foreground">
        <p>{t("masksAndZones.zones.desc.title")}</p>
      </div>
      <Separator className="my-3 bg-secondary" />
      {polygons && activePolygonIndex !== undefined && (
        <div className="my-2 flex w-full flex-row justify-between text-sm">
          <div className="my-1 inline-flex">
            {t("masksAndZones.zones.point", {
              count: polygons[activePolygonIndex].points.length,
            })}

            {polygons[activePolygonIndex].isFinished && (
              <FaCheckCircle className="ml-2 size-5" />
            )}
          </div>
          <PolygonEditControls
            polygons={polygons}
            setPolygons={setPolygons}
            activePolygonIndex={activePolygonIndex}
            snapPoints={snapPoints}
            setSnapPoints={setSnapPoints}
          />
        </div>
      )}
      <div className="mb-3 text-sm text-muted-foreground">
        {t("masksAndZones.zones.clickDrawPolygon")}
      </div>

      <Separator className="my-3 bg-secondary" />

      <Form {...form}>
        <form onSubmit={form.handleSubmit(onSubmit)} className="mt-2 space-y-6">
          <NameAndIdFields
            type="zone"
            control={form.control}
            nameField="friendly_name"
            idField="name"
            idVisible={(polygon && polygon.name.length > 0) ?? false}
            nameLabel={t("masksAndZones.zones.name.title")}
            nameDescription={t("masksAndZones.zones.name.tips")}
            placeholderName={t("masksAndZones.zones.name.inputPlaceHolder")}
            idDisabled={idDisabled}
          />
          <FormField
            control={form.control}
            name="enabled"
            render={({ field }) => (
              <FormItem className="flex flex-row items-center justify-between gap-3">
                <div className="space-y-0.5">
                  <FormLabel>
                    {t("masksAndZones.zones.enabled.title")}
                  </FormLabel>
                  <FormDescription>
                    {t("masksAndZones.zones.enabled.description")}
                  </FormDescription>
                </div>
                <FormControl>
                  <Switch
                    checked={field.value}
                    onCheckedChange={field.onChange}
                  />
                </FormControl>
              </FormItem>
            )}
          />

          <Separator className="my-2 flex bg-secondary" />
          <FormField
            control={form.control}
            name="inertia"
            render={({ field }) => (
              <FormItem>
                <FormLabel>{t("masksAndZones.zones.inertia.title")}</FormLabel>
                <FormControl>
                  <Input
                    className="w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
                    placeholder="3"
                    {...field}
                  />
                </FormControl>
                <FormDescription>
                  <Trans ns="views/settings">
                    masksAndZones.zones.inertia.desc
                  </Trans>
                </FormDescription>
                <FormMessage />
              </FormItem>
            )}
          />
          <Separator className="my-2 flex bg-secondary" />
          <FormField
            control={form.control}
            name="loitering_time"
            render={({ field }) => (
              <FormItem>
                <FormLabel>
                  {t("masksAndZones.zones.loiteringTime.title")}
                </FormLabel>
                <FormControl>
                  <Input
                    className="w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
                    placeholder="0"
                    {...field}
                  />
                </FormControl>
                <FormDescription>
                  <Trans ns="views/settings">
                    masksAndZones.zones.loiteringTime.desc
                  </Trans>
                </FormDescription>
                <FormMessage />
              </FormItem>
            )}
          />
          <Separator className="my-2 flex bg-secondary" />
          <FormItem>
            <FormLabel>{t("masksAndZones.zones.objects.title")}</FormLabel>
            <FormDescription>
              {t("masksAndZones.zones.objects.desc")}
            </FormDescription>
            <ZoneObjectSelector
              camera={polygon.camera}
              zoneName={polygon.name}
              selectedLabels={polygon.objects}
              updateLabelFilter={(objects) => {
                if (activePolygonIndex === undefined || !polygons) {
                  return;
                }
                const updatedPolygons = [...polygons];
                const activePolygon = updatedPolygons[activePolygonIndex];
                updatedPolygons[activePolygonIndex] = {
                  ...activePolygon,
                  objects: objects ?? [],
                };
                setPolygons(updatedPolygons);
              }}
            />
          </FormItem>

          <Separator className="my-2 flex bg-secondary" />
          <FormField
            control={form.control}
            name="speedEstimation"
            render={({ field }) => (
              <FormItem>
                <div className="flex items-center space-x-2">
                  <FormControl>
                    <div className="my-2.5 flex w-full items-center justify-between">
                      <FormLabel
                        className="cursor-pointer text-primary"
                        htmlFor="allLabels"
                      >
                        {t("masksAndZones.zones.speedEstimation.title")}
                      </FormLabel>
                      <Switch
                        checked={field.value}
                        onCheckedChange={(checked) => {
                          if (
                            checked &&
                            polygons &&
                            activePolygonIndex &&
                            polygons[activePolygonIndex].points.length !== 4
                          ) {
                            toast.error(
                              t(
                                "masksAndZones.zones.speedThreshold.toast.error.pointLengthError",
                              ),
                            );
                            return;
                          }
                          const loiteringTime =
                            form.getValues("loitering_time");

                          if (checked && loiteringTime && loiteringTime > 0) {
                            toast.error(
                              t(
                                "masksAndZones.zones.speedThreshold.toast.error.loiteringTimeError",
                              ),
                            );
                          }
                          field.onChange(checked);
                        }}
                      />
                    </div>
                  </FormControl>
                </div>
                <FormDescription>
                  {t("masksAndZones.zones.speedEstimation.desc")}
                  <span className="mt-2 flex items-center text-primary">
                    <Link
                      to={getLocaleDocUrl(
                        "configuration/zones#speed-estimation",
                      )}
                      target="_blank"
                      rel="noopener noreferrer"
                      className="inline"
                    >
                      {t("readTheDocumentation", { ns: "common" })}
                      <LuExternalLink className="ml-2 inline-flex size-3" />
                    </Link>
                  </span>
                </FormDescription>
                <FormMessage />
              </FormItem>
            )}
          />

          {form.watch("speedEstimation") &&
            polygons &&
            activePolygonIndex !== undefined &&
            polygons[activePolygonIndex].points.length === 4 && (
              <>
                <FormField
                  control={form.control}
                  name="lineA"
                  render={({ field }) => (
                    <FormItem>
                      <FormLabel>
                        {t(
                          "masksAndZones.zones.speedEstimation.lineADistance",
                          {
                            unit:
                              config?.ui.unit_system == "imperial"
                                ? t("unit.length.feet", { ns: "common" })
                                : t("unit.length.meters", { ns: "common" }),
                          },
                        )}
                      </FormLabel>
                      <FormControl>
                        <Input
                          className="w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
                          {...field}
                          onFocus={() => setActiveLine(1)}
                          onBlur={() => setActiveLine(undefined)}
                        />
                      </FormControl>
                    </FormItem>
                  )}
                />
                <FormField
                  control={form.control}
                  name="lineB"
                  render={({ field }) => (
                    <FormItem>
                      <FormLabel>
                        {t(
                          "masksAndZones.zones.speedEstimation.lineBDistance",
                          {
                            unit:
                              config?.ui.unit_system == "imperial"
                                ? t("unit.length.feet", { ns: "common" })
                                : t("unit.length.meters", { ns: "common" }),
                          },
                        )}
                      </FormLabel>
                      <FormControl>
                        <Input
                          className="w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
                          {...field}
                          onFocus={() => setActiveLine(2)}
                          onBlur={() => setActiveLine(undefined)}
                        />
                      </FormControl>
                    </FormItem>
                  )}
                />
                <FormField
                  control={form.control}
                  name="lineC"
                  render={({ field }) => (
                    <FormItem>
                      <FormLabel>
                        {t(
                          "masksAndZones.zones.speedEstimation.lineCDistance",
                          {
                            unit:
                              config?.ui.unit_system == "imperial"
                                ? t("unit.length.feet", { ns: "common" })
                                : t("unit.length.meters", { ns: "common" }),
                          },
                        )}
                      </FormLabel>
                      <FormControl>
                        <Input
                          className="w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
                          {...field}
                          onFocus={() => setActiveLine(3)}
                          onBlur={() => setActiveLine(undefined)}
                        />
                      </FormControl>
                    </FormItem>
                  )}
                />
                <FormField
                  control={form.control}
                  name="lineD"
                  render={({ field }) => (
                    <FormItem>
                      <FormLabel>
                        {t(
                          "masksAndZones.zones.speedEstimation.lineDDistance",
                          {
                            unit:
                              config?.ui.unit_system == "imperial"
                                ? t("unit.length.feet", { ns: "common" })
                                : t("unit.length.meters", { ns: "common" }),
                          },
                        )}
                      </FormLabel>
                      <FormControl>
                        <Input
                          className="w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
                          {...field}
                          onFocus={() => setActiveLine(4)}
                          onBlur={() => setActiveLine(undefined)}
                        />
                      </FormControl>
                    </FormItem>
                  )}
                />

                <Separator className="my-2 flex bg-secondary" />
                <FormField
                  control={form.control}
                  name="speed_threshold"
                  render={({ field }) => (
                    <FormItem>
                      <FormLabel>
                        {t("masksAndZones.zones.speedThreshold.title", {
                          ns: "views/settings",
                          unit:
                            config?.ui.unit_system == "imperial"
                              ? t("unit.speed.mph", { ns: "common" })
                              : t("unit.speed.kph", { ns: "common" }),
                        })}
                      </FormLabel>
                      <FormControl>
                        <Input
                          className="w-full border border-input bg-background p-2 hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
                          {...field}
                        />
                      </FormControl>
                      <FormDescription>
                        {t("masksAndZones.zones.speedThreshold.desc")}
                      </FormDescription>
                      <FormMessage />
                    </FormItem>
                  )}
                />
              </>
            )}

          <FormField
            control={form.control}
            name="isFinished"
            render={() => (
              <FormItem>
                <FormMessage />
              </FormItem>
            )}
          />
          <div className="flex flex-row gap-2 pt-5">
            <Button
              className="flex flex-1"
              aria-label={t("button.cancel", { ns: "common" })}
              onClick={onCancel}
            >
              {t("button.cancel", { ns: "common" })}
            </Button>
            <Button
              variant="select"
              disabled={isLoading || !canSave}
              className="flex flex-1"
              aria-label={t("button.save", { ns: "common" })}
              type="submit"
            >
              {isLoading ? (
                <div className="flex flex-row items-center gap-2">
                  <ActivityIndicator className="size-4" />
                  <span>{t("button.saving", { ns: "common" })}</span>
                </div>
              ) : (
                t("button.save", { ns: "common" })
              )}
            </Button>
          </div>
        </form>
      </Form>
    </>
  );
}

type ZoneObjectSelectorProps = {
  camera: string;
  zoneName: string;
  selectedLabels: string[];
  updateLabelFilter: (labels: string[] | undefined) => void;
};

export function ZoneObjectSelector({
  camera,
  zoneName,
  selectedLabels,
  updateLabelFilter,
}: ZoneObjectSelectorProps) {
  const { t } = useTranslation(["views/settings"]);
  const { data: config } = useSWR<FrigateConfig>("config");

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

    return getAttributeLabels(config);
  }, [config]);

  const cameraConfig = useMemo(() => {
    if (config && camera) {
      return config.cameras[camera];
    }
  }, [config, camera]);

  const allLabels = useMemo<string[]>(() => {
    if (!cameraConfig || !config) {
      return [];
    }

    const labels = new Set<string>();

    cameraConfig.objects.track.forEach((label) => {
      if (!attributeLabels.includes(label)) {
        labels.add(label);
      }
    });

    if (zoneName) {
      if (cameraConfig.zones[zoneName]) {
        cameraConfig.zones[zoneName].objects.forEach((label) => {
          if (!attributeLabels.includes(label)) {
            labels.add(label);
          }
        });
      }
    }

    return [...labels].sort() || [];
  }, [config, cameraConfig, attributeLabels, zoneName]);

  const [currentLabels, setCurrentLabels] = useState<string[] | undefined>(
    selectedLabels,
  );

  useEffect(() => {
    updateLabelFilter(currentLabels);
    // we know that these deps are correct
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [currentLabels]);

  return (
    <>
      <div className="scrollbar-container h-auto overflow-y-auto overflow-x-hidden">
        <div className="my-2.5 flex items-center justify-between">
          <Label className="cursor-pointer text-primary" htmlFor="allLabels">
            {t("masksAndZones.zones.allObjects")}
          </Label>
          <Switch
            className="ml-1"
            id="allLabels"
            checked={!currentLabels?.length}
            onCheckedChange={(isChecked) => {
              if (isChecked) {
                setCurrentLabels([]);
              }
            }}
          />
        </div>
        <Separator />
        <div className="my-2.5 flex flex-col gap-2.5">
          {allLabels.map((item) => (
            <div key={item} className="flex items-center justify-between">
              <Label
                className="w-full cursor-pointer text-primary smart-capitalize"
                htmlFor={item}
              >
                {getTranslatedLabel(item)}
              </Label>
              <Switch
                key={item}
                className="ml-1"
                id={item}
                checked={currentLabels?.includes(item) ?? false}
                onCheckedChange={(isChecked) => {
                  if (isChecked) {
                    const updatedLabels = currentLabels
                      ? [...currentLabels]
                      : [];

                    updatedLabels.push(item);
                    setCurrentLabels(updatedLabels);
                  } else {
                    const updatedLabels = currentLabels
                      ? [...currentLabels]
                      : [];

                    // can not deselect the last item
                    if (updatedLabels.length > 1) {
                      updatedLabels.splice(updatedLabels.indexOf(item), 1);
                      setCurrentLabels(updatedLabels);
                    }
                  }
                }}
              />
            </div>
          ))}
        </div>
      </div>
    </>
  );
}
