"use client";

import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { type ServiceItem } from "@/core/api/services/other-services";
import { type TagItem } from "@/core/api/services/tags";
import { getFixPackageAction } from "@/features/packages/actions/fix-packages-actions";
import {
  deleteFixPackageItinerarySectionAction,
  updateFixPackageExtrasSectionAction,
  updateFixPackageItinerarySectionAction,
  updateFixPackageOverviewSectionAction,
  updateFixPackageSliderImagesSectionAction,
} from "@/features/packages/actions/fix-package-section-actions";
import {
  createTagAction,
  getTagsAction,
} from "@/features/packages/actions/tags-actions";
import type {
  CityOption,
  DayEntry,
  ExtrasEntry,
  SightseeingItem,
} from "@/features/packages/types/package-form.types";
import { useToastEnhanced } from "@/hooks/use-toast-enhanced";
import { getStorageUrl } from "@/lib/utils";
import { FIX_PACKAGES_ADMIN } from "@/lib/strings";
import type {
  FixPackageInput,
  PackageHotelInput,
  PackageImageInput,
  PackageItineraryInput,
  PackageVehicleInput,
} from "@/lib/validations/fix-packages";
import { ArrowLeft, Pencil, Plus, Upload, X } from "lucide-react";
import { useRouter } from "next/navigation";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useForm } from "react-hook-form";
import { HotelDialog, VehicleDialog } from "./dialogs";
import {
  AddTagModal,
  createValidateImageSize,
  ExtrasCard,
  ItineraryDetailsCard,
  OtherServicesCard,
  PackageInfoCard,
  SightseeingModal,
  TagsCard,
} from "./form-sections";
import type { ApiPackage } from "./lib";
import { mapApiPackageToFormState } from "./lib";

const packageFetchCache = new Map<string, Promise<any>>();

function fetchPackageOnce(id: string) {
  if (!packageFetchCache.has(id)) {
    const promise = getFixPackageAction(id).finally(() =>
      packageFetchCache.delete(id)
    );
    packageFetchCache.set(id, promise);
  }
  return packageFetchCache.get(id)!;
}

type UpdateTab = "overview" | "slider" | "itinerary" | "extras";

interface PackageUpdateClientProps {
  packageId: string;
  initialTags?: TagItem[];
  initialServices?: ServiceItem[];
  initialCities?: CityOption[];
}

function BannerImageCard({
  bannerPreview,
  onBannerChange,
}: {
  bannerPreview: string | null;
  onBannerChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
}) {
  const bannerInputRef = useRef<HTMLInputElement>(null);

  return (
    <Card>
      <CardHeader>
        <CardTitle>{FIX_PACKAGES_ADMIN.BANNER_IMAGE}</CardTitle>
      </CardHeader>
      <CardContent>
        <div
          className="aspect-[32/9] bg-muted flex items-center justify-center border-2 border-dashed rounded-lg cursor-pointer overflow-hidden"
          onClick={() => bannerInputRef.current?.click()}
        >
          {bannerPreview ? (
            <img
              src={bannerPreview}
              alt={FIX_PACKAGES_ADMIN.BANNER_PREVIEW_ALT}
              className="w-full h-full object-cover"
            />
          ) : (
            <div className="text-center">
              <Upload className="mx-auto h-8 w-8 text-muted-foreground mb-2" />
              <p className="text-xs text-muted-foreground">
                {FIX_PACKAGES_ADMIN.CLICK_TO_UPLOAD_BANNER}
              </p>
            </div>
          )}
        </div>
        <input
          type="file"
          ref={bannerInputRef}
          className="hidden"
          accept="image/*"
          onChange={onBannerChange}
        />
      </CardContent>
    </Card>
  );
}

function SliderImagePreview({
  image,
  alt,
}: {
  image: PackageImageInput["image"];
  alt: string;
}) {
  const [previewUrl, setPreviewUrl] = useState<string | null>(null);

  useEffect(() => {
    if (!image) {
      setPreviewUrl(null);
      return;
    }

    if (image instanceof File) {
      const objectUrl = URL.createObjectURL(image);
      setPreviewUrl(objectUrl);
      return () => URL.revokeObjectURL(objectUrl);
    }

    setPreviewUrl(getStorageUrl(String(image)));
  }, [image]);

  if (!previewUrl) return null;

  return (
    <img src={previewUrl} alt={alt} className="w-full h-full object-cover" />
  );
}

function SliderImagesCard({
  extraImages,
  onSliderImagesChange,
  onDeleteImage,
  onEditImage,
}: {
  extraImages: PackageImageInput[];
  onSliderImagesChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
  onDeleteImage: (index: number) => void;
  onEditImage: (index: number, file: File) => void;
}) {
  const sliderInputRef = useRef<HTMLInputElement>(null);
  const editInputRef = useRef<HTMLInputElement>(null);
  const editingIndexRef = useRef<number>(-1);

  const handleEditClick = (index: number) => {
    editingIndexRef.current = index;
    editInputRef.current?.click();
  };

  const handleEditFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (file && editingIndexRef.current !== -1) {
      onEditImage(editingIndexRef.current, file);
    }
    e.target.value = "";
    editingIndexRef.current = -1;
  };

  return (
    <Card>
      <CardHeader className="flex flex-row items-center justify-between">
        <CardTitle>{FIX_PACKAGES_ADMIN.SLIDER_IMAGES}</CardTitle>
        <Button
          type="button"
          variant="outline"
          size="sm"
          onClick={() => sliderInputRef.current?.click()}
          disabled={extraImages.length >= 6}
        >
          <Plus className="mr-2 h-4 w-4" />
          {FIX_PACKAGES_ADMIN.ADD_SLIDER_IMAGE}
          {extraImages.length > 0
            ? FIX_PACKAGES_ADMIN.SLIDER_IMAGE_COUNT.replace(
                "{count}",
                String(extraImages.length)
              )
            : ""}
        </Button>
      </CardHeader>
      <CardContent className="space-y-4">
        <input
          type="file"
          ref={sliderInputRef}
          className="hidden"
          accept="image/*"
          multiple
          onChange={onSliderImagesChange}
        />
        <input
          type="file"
          ref={editInputRef}
          className="hidden"
          accept="image/*"
          onChange={handleEditFileChange}
        />
        {extraImages.length > 0 ? (
          <div className="grid grid-cols-2 gap-3 md:grid-cols-3 xl:grid-cols-6">
            {extraImages.map((img, index) => (
              <div
                key={`${img.id ?? "new"}-${index}`}
                className="relative group aspect-square bg-muted rounded-md overflow-hidden"
              >
                <SliderImagePreview
                  image={img.image}
                  alt={`Slider image ${index + 1}`}
                />
                <Button
                  size="icon"
                  variant="ghost"
                  type="button"
                  className="absolute top-1 left-1 h-6 w-6 bg-black/60 hover:bg-black/80 opacity-0 group-hover:opacity-100 transition-opacity"
                  onClick={() => handleEditClick(index)}
                >
                  <Pencil className="h-3 w-3 text-white" />
                </Button>
                <Button
                  size="icon"
                  variant="ghost"
                  type="button"
                  className="absolute top-1 right-1 h-6 w-6 bg-black/60 hover:bg-black/80 opacity-0 group-hover:opacity-100 transition-opacity"
                  onClick={() => onDeleteImage(index)}
                >
                  <X className="h-3 w-3 text-white" />
                </Button>
              </div>
            ))}
          </div>
        ) : (
          <div className="rounded-lg border border-dashed bg-muted/30 p-8 text-center text-sm text-muted-foreground">
            No slider images added yet.
          </div>
        )}
      </CardContent>
    </Card>
  );
}

function TabFooter({
  isSaving,
  onCancel,
  onSave,
  saveLabel,
}: {
  isSaving: boolean;
  onCancel: () => void;
  onSave: () => void;
  saveLabel: string;
}) {
  return (
    <div className="flex justify-end gap-4 border-t bg-background/50 p-6 sticky bottom-0 z-10">
      <Button type="button" variant="outline" onClick={onCancel}>
        {FIX_PACKAGES_ADMIN.CANCEL_BUTTON}
      </Button>
      <Button type="button" onClick={onSave} disabled={isSaving}>
        {isSaving ? FIX_PACKAGES_ADMIN.SAVING : saveLabel}
      </Button>
    </div>
  );
}

export function PackageUpdateClient({
  packageId,
  initialTags = [],
  initialServices = [],
  initialCities = [],
}: PackageUpdateClientProps) {
  const router = useRouter();
  const toast = useToastEnhanced();
  const [activeTab, setActiveTab] = useState<UpdateTab>("overview");
  const [isPageLoading, setIsPageLoading] = useState(true);
  const [savingSection, setSavingSection] = useState<UpdateTab | null>(null);
  const [savingItineraryDayId, setSavingItineraryDayId] = useState<
    number | null
  >(null);
  const [deletingItineraryDayId, setDeletingItineraryDayId] = useState<
    number | null
  >(null);

  const [vehicleTargetDayId, setVehicleTargetDayId] = useState<number | null>(
    null
  );
  const [hotelTargetDayId, setHotelTargetDayId] = useState<number | null>(null);
  const [isVehicleDialogOpen, setIsVehicleDialogOpen] = useState(false);
  const [isHotelDialogOpen, setIsHotelDialogOpen] = useState(false);
  const [editingVehicleIndex, setEditingVehicleIndex] = useState<number | null>(
    null
  );
  const [editingHotelIndex, setEditingHotelIndex] = useState<number | null>(
    null
  );

  const [availableTags, setAvailableTags] = useState<TagItem[]>(initialTags);
  const [tagsLoading, setTagsLoading] = useState(false);
  const [selectedTagIds, setSelectedTagIds] = useState<number[]>([]);
  const [isAddTagModalOpen, setIsAddTagModalOpen] = useState(false);
  const [newTagName, setNewTagName] = useState("");
  const [isTagSaving, setIsTagSaving] = useState(false);

  const availableServices = initialServices;
  const servicesLoading = false;
  const citiesLoading = false;
  const hasSeedCities = initialCities.length > 0;
  const nextLocalIdRef = useRef(2);

  const createLocalId = useCallback(() => {
    const nextId = nextLocalIdRef.current;
    nextLocalIdRef.current += 1;
    return nextId;
  }, []);

  const [extraImages, setExtraImages] = useState<PackageImageInput[]>([]);
  const [bannerPreview, setBannerPreview] = useState<string | null>(null);
  const [extrasEntries, setExtrasEntries] = useState<ExtrasEntry[]>([
    { id: 1, title: "", description: "" },
  ]);
  const [dayEntries, setDayEntries] = useState<DayEntry[]>([]);
  const [collapsedDayIds, setCollapsedDayIds] = useState<Set<number>>(
    new Set()
  );

  const [isSightseeingModalOpen, setIsSightseeingModalOpen] = useState(false);
  const [sightseeingTargetId, setSightseeingTargetId] = useState<number | null>(
    null
  );
  const [sightseeingDraft, setSightseeingDraft] = useState<SightseeingItem[]>(
    []
  );

  const extrasRefs = useRef<(HTMLDivElement | null)[]>([]);
  const itineraryRefs = useRef<(HTMLDivElement | null)[]>([]);

  const makeDefaultDay = useCallback((dayNum: number): DayEntry => {
    return {
      id: Date.now() + dayNum,
      isDayEntry: true,
      includeNight: false,
      day: dayNum,
      day_title: "",
      day_city: "",
      city_id: null,
      staying_location: "",
      meal_breakfast: false,
      meal_lunch: false,
      meal_dinner: false,
      sightseeings: [],
      description: "",
      vehicles: [],
      hotels: [],
    };
  }, []);

  const {
    register,
    setValue,
    watch,
    setError,
    getValues,
    formState: { errors },
  } = useForm<FixPackageInput>({
    defaultValues: {
      name: "",
      location_detail: "",
      other_detail: "",
      sort: null,
      price: 0,
      discount_price: 0,
      total_days: 1,
      total_nights: 0,
      status: "active",
      package_type: "normal",
      package_category: "fix_package",
      add_more_images: false,
      start_date: "",
      end_date: "",
      vehicles: [],
      hotels: [],
      images: [],
      tag_ids: [],
      service_ids: [],
      itineraries: [],
      extras: [],
      inclusion: "",
      exclusion: "",
    },
  });

  const updateDay = useCallback((id: number, patch: Partial<DayEntry>) => {
    setDayEntries((prev) =>
      prev.map((entry) => (entry.id === id ? { ...entry, ...patch } : entry))
    );
  }, []);

  const updateExtras = useCallback(
    (id: number, patch: Partial<ExtrasEntry>) => {
      setExtrasEntries((prev) =>
        prev.map((entry) => (entry.id === id ? { ...entry, ...patch } : entry))
      );
    },
    []
  );

  const removeExtras = useCallback((id: number) => {
    setExtrasEntries((prev) => prev.filter((entry) => entry.id !== id));
  }, []);

  const hydratePackage = useCallback(async () => {
    setIsPageLoading(true);
    try {
      const result = await fetchPackageOnce(packageId);
      if (!result.success || !result.data) {
        throw new Error(
          result.error || FIX_PACKAGES_ADMIN.FAILED_TO_LOAD_PACKAGE_DETAILS
        );
      }

      const update = mapApiPackageToFormState(
        result.data as ApiPackage,
        initialTags,
        initialServices,
        getStorageUrl
      );

      Object.entries(update.formValues).forEach(([key, value]) => {
        setValue(key as keyof FixPackageInput, value as never);
      });

      setBannerPreview(update.bannerPreview);
      setSelectedTagIds(update.selectedTagIds);
      setExtrasEntries(
        update.extrasEntries.length > 0
          ? update.extrasEntries
          : [{ id: createLocalId(), title: "", description: "" }]
      );
      setDayEntries(
        update.dayEntries.length > 0 ? update.dayEntries : [makeDefaultDay(1)]
      );
      setExtraImages(update.extraImages);
    } catch (error) {
      toast.error({
        title: FIX_PACKAGES_ADMIN.ERROR,
        description:
          error instanceof Error
            ? error.message
            : FIX_PACKAGES_ADMIN.FAILED_TO_LOAD_PACKAGE_DETAILS,
      });
    } finally {
      setIsPageLoading(false);
    }
  }, [
    packageId,
    initialServices,
    initialTags,
    makeDefaultDay,
    setValue,
    toast,
    createLocalId,
  ]);

  useEffect(() => {
    void hydratePackage();
  }, [hydratePackage]);

  useEffect(() => {
    setValue(
      "total_days",
      dayEntries.filter((entry) => entry.isDayEntry).length
    );
    setValue(
      "total_nights",
      dayEntries.filter((entry) => entry.includeNight).length
    );
    setValue(
      "vehicles",
      dayEntries.flatMap((entry) => entry.vehicles)
    );
    setValue(
      "hotels",
      dayEntries.flatMap((entry) => entry.hotels)
    );
  }, [dayEntries, setValue]);

  useEffect(() => {
    setValue("images", extraImages);
  }, [extraImages, setValue]);

  useEffect(() => {
    setValue(
      "extras",
      extrasEntries.map(({ title, description }) => ({ title, description }))
    );
  }, [extrasEntries, setValue]);

  useEffect(() => {
    const itineraries: PackageItineraryInput[] = dayEntries.map((entry) => ({
      day: entry.day,
      title: entry.day_title,
      city_id: entry.city_id ?? (undefined as unknown as number),
      night_stay_location: entry.staying_location || undefined,
      is_day_plan: entry.isDayEntry ? "1" : "0",
      is_night_plan: entry.includeNight ? "1" : "0",
      description: entry.description || undefined,
      is_breakfast: entry.meal_breakfast ? "1" : "0",
      is_lunch: entry.meal_lunch ? "1" : "0",
      is_dinner: entry.meal_dinner ? "1" : "0",
      status: "active",
      sight_seeing_ids: entry.sightseeings.map((item) => item.id),
      vehicles: entry.vehicles,
      hotels: entry.hotels,
    }));

    setValue("itineraries", itineraries);
  }, [dayEntries, setValue]);

  const fetchTags = useCallback(async () => {
    setTagsLoading(true);
    try {
      const result = await getTagsAction();
      if (result.success && result.data) {
        setAvailableTags(result.data);
      }
    } finally {
      setTagsLoading(false);
    }
  }, []);

  const validateImageSize = useMemo(
    () => createValidateImageSize(toast),
    [toast]
  );

  const normalizeFieldPath = useCallback((path: string) => {
    return path.replace(/\]?\[/g, ".").replace(/\]/g, "");
  }, []);

  const handleActionError = useCallback(
    (
      error:
        | {
            message?: string;
            error?: string;
            errors?: Record<string, string[]>;
          }
        | Error,
      tab: UpdateTab
    ) => {
      const message =
        "error" in error
          ? error.error
          : "message" in error
            ? error.message
            : FIX_PACKAGES_ADMIN.ERROR_SAVING_PACKAGE;

      const fieldErrors =
        typeof error === "object" && error && "errors" in error
          ? error.errors
          : undefined;

      if (fieldErrors) {
        Object.entries(fieldErrors).forEach(([rawPath, messages]) => {
          const fieldPath = normalizeFieldPath(rawPath);
          const firstMessage = Array.isArray(messages) ? messages[0] : messages;
          setError(fieldPath as keyof FixPackageInput, {
            type: "server",
            message: firstMessage,
          });

          const itineraryMatch = /^itineraries\.(\d+)/.exec(fieldPath);
          if (
            tab === "itinerary" &&
            itineraryMatch &&
            dayEntries[Number(itineraryMatch[1])]
          ) {
            setCollapsedDayIds((prev) => {
              const next = new Set(prev);
              next.delete(dayEntries[Number(itineraryMatch[1])].id);
              return next;
            });
          }
        });
      }

      toast.error({
        title: FIX_PACKAGES_ADMIN.FAILED_TO_SAVE_PACKAGE,
        description: message || FIX_PACKAGES_ADMIN.ERROR_SAVING_PACKAGE,
      });
    },
    [dayEntries, normalizeFieldPath, setError, toast]
  );

  const saveOverview = useCallback(async () => {
    setSavingSection("overview");
    try {
      const values = getValues();
      console.log("==> values", JSON.stringify(values, null, 2));
      const result = await updateFixPackageOverviewSectionAction({
        id: packageId,
        name: values.name,
        location_detail: values.location_detail || "",
        other_detail: values.other_detail || "",
        sort: values.sort ?? null,
        price: Number(values.price || 0),
        discount_price: values.discount_price ?? null,
        total_days: dayEntries.filter((entry) => entry.isDayEntry).length,
        total_nights: dayEntries.filter((entry) => entry.includeNight).length,
        end_date: values.end_date,
        add_more_images: extraImages.some((image) => image.status !== 0),
        status: values.status,
        package_type: values.package_type,
        package_category: values.package_category,
        inclusion: values.inclusion || "",
        exclusion: values.exclusion || "",
        banner_image:
          values.banner_image instanceof File ? values.banner_image : null,
        tag_ids: selectedTagIds,
        service_ids: values.service_ids || [],
      });

      if (!result.success) {
        throw result;
      }

      toast.success({
        title: FIX_PACKAGES_ADMIN.PACKAGE_UPDATED,
        description: "Overview details updated successfully.",
      });
      await hydratePackage();
    } catch (error) {
      handleActionError(error as any, "overview");
    } finally {
      setSavingSection(null);
    }
  }, [
    getValues,
    handleActionError,
    hydratePackage,
    dayEntries,
    extraImages,
    packageId,
    selectedTagIds,
    toast,
  ]);

  const saveSliderImages = useCallback(async () => {
    setSavingSection("slider");
    try {
      const result = await updateFixPackageSliderImagesSectionAction(
        packageId,
        extraImages
      );

      if (!result.success) {
        throw result;
      }

      toast.success({
        title: FIX_PACKAGES_ADMIN.PACKAGE_UPDATED,
        description: "Slider images updated successfully.",
      });
      await hydratePackage();
    } catch (error) {
      handleActionError(error as any, "slider");
    } finally {
      setSavingSection(null);
    }
  }, [extraImages, handleActionError, hydratePackage, packageId, toast]);

  const saveItineraryDay = useCallback(
    async (dayId: number) => {
      setSavingItineraryDayId(dayId);
      try {
        const updatedDayEntries = dayEntries.map((day, index) => ({
          ...day,
          description:
            itineraryRefs.current?.[index]?.querySelector(".ql-editor")
              ?.innerHTML ?? day.description,
        }));

        const targetDay = updatedDayEntries.find((day) => day.id === dayId);
        if (!targetDay) return;

        const result = await updateFixPackageItinerarySectionAction(packageId, [
          targetDay,
        ]);

        if (!result.success) {
          throw result;
        }

        toast.success({
          title: FIX_PACKAGES_ADMIN.PACKAGE_UPDATED,
          description: "Itinerary details updated successfully.",
        });
        await hydratePackage();
      } catch (error) {
        handleActionError(error as any, "itinerary");
      } finally {
        setSavingItineraryDayId(null);
      }
    },
    [dayEntries, handleActionError, hydratePackage, packageId, toast]
  );

  const handleDeleteDay = useCallback(
    async (dayId: number) => {
      const day = dayEntries.find((d) => d.id === dayId);
      if (!day) return;

      if (day.apiId != null) {
        setDeletingItineraryDayId(dayId);
        try {
          const result = await deleteFixPackageItinerarySectionAction(
            day.apiId
          );
          if (!result.success) {
            throw result;
          }
          toast.success({
            title: FIX_PACKAGES_ADMIN.PACKAGE_UPDATED,
            description: "Itinerary day deleted successfully.",
          });
        } catch (error) {
          handleActionError(error as any, "itinerary");
          return;
        } finally {
          setDeletingItineraryDayId(null);
        }
      }

      setDayEntries((prev) => prev.filter((d) => d.id !== dayId));
    },
    [dayEntries, handleActionError, toast]
  );

  const saveExtras = useCallback(async () => {
    setSavingSection("extras");
    try {
      const updatedExtras = extrasEntries.map((extra, index) => ({
        ...extra,
        description:
          extrasRefs.current?.[index]?.querySelector(".ql-editor")?.innerHTML ??
          extra.description,
      }));

      const result = await updateFixPackageExtrasSectionAction(
        packageId,
        updatedExtras
      );

      if (!result.success) {
        throw result;
      }

      toast.success({
        title: FIX_PACKAGES_ADMIN.PACKAGE_UPDATED,
        description: "Extras updated successfully.",
      });
      await hydratePackage();
    } catch (error) {
      handleActionError(error as any, "extras");
    } finally {
      setSavingSection(null);
    }
  }, [extrasEntries, handleActionError, hydratePackage, packageId, toast]);

  const handleAddTag = useCallback(
    async (trimmedName: string) => {
      setIsTagSaving(true);
      try {
        const result = await createTagAction(trimmedName);
        if (!result.success) {
          throw new Error(result.error || "Failed to create tag");
        }

        await fetchTags();
        toast.success({
          title: "Tag Added",
          description: `${trimmedName} has been added successfully.`,
        });
      } catch (error) {
        toast.error({
          title: FIX_PACKAGES_ADMIN.ERROR,
          description:
            error instanceof Error ? error.message : "Failed to create tag",
        });
      } finally {
        setIsTagSaving(false);
      }
    },
    [fetchTags, toast]
  );

  const handleAddVehicle = useCallback(
    (vehicle: PackageVehicleInput) => {
      if (vehicleTargetDayId === null) return;
      updateDay(vehicleTargetDayId, {
        vehicles: [
          ...(dayEntries.find((day) => day.id === vehicleTargetDayId)
            ?.vehicles ?? []),
          vehicle,
        ],
      });
      setIsVehicleDialogOpen(false);
    },
    [dayEntries, updateDay, vehicleTargetDayId]
  );

  const handleEditVehicle = useCallback(
    (vehicle: PackageVehicleInput) => {
      if (vehicleTargetDayId === null || editingVehicleIndex === null) return;
      const day = dayEntries.find((entry) => entry.id === vehicleTargetDayId);
      if (!day) return;

      const updatedVehicles = [...day.vehicles];
      updatedVehicles[editingVehicleIndex] = vehicle;
      updateDay(vehicleTargetDayId, { vehicles: updatedVehicles });
      setEditingVehicleIndex(null);
      setIsVehicleDialogOpen(false);
    },
    [dayEntries, editingVehicleIndex, updateDay, vehicleTargetDayId]
  );

  const handleAddHotel = useCallback(
    (hotel: PackageHotelInput) => {
      if (hotelTargetDayId === null) return;
      updateDay(hotelTargetDayId, {
        hotels: [
          ...(dayEntries.find((day) => day.id === hotelTargetDayId)?.hotels ??
            []),
          hotel,
        ],
      });
      setIsHotelDialogOpen(false);
    },
    [dayEntries, hotelTargetDayId, updateDay]
  );

  const handleEditHotel = useCallback(
    (hotel: PackageHotelInput) => {
      if (hotelTargetDayId === null || editingHotelIndex === null) return;
      const day = dayEntries.find((entry) => entry.id === hotelTargetDayId);
      if (!day) return;

      const updatedHotels = [...day.hotels];
      updatedHotels[editingHotelIndex] = hotel;
      updateDay(hotelTargetDayId, { hotels: updatedHotels });
      setEditingHotelIndex(null);
      setIsHotelDialogOpen(false);
    },
    [dayEntries, editingHotelIndex, hotelTargetDayId, updateDay]
  );

  const handleDeleteImage = useCallback(
    (index: number) => {
      const visibleImages = extraImages.filter((image) => image.status !== 0);
      const target = visibleImages[index];
      if (!target) return;

      const actualIndex = extraImages.indexOf(target);
      if (actualIndex === -1) return;

      const updatedImages = target.id
        ? extraImages.map((image, imageIndex) =>
            imageIndex === actualIndex
              ? { ...image, status: 0 as const }
              : image
          )
        : extraImages.filter((_, imageIndex) => imageIndex !== actualIndex);

      setExtraImages(updatedImages);
      setValue("images", updatedImages);
    },
    [extraImages, setValue]
  );

  const handleBannerChange = useCallback(
    (event: React.ChangeEvent<HTMLInputElement>) => {
      const file = event.target.files?.[0];
      if (!file) return;
      if (!validateImageSize(file)) {
        event.target.value = "";
        return;
      }

      if (bannerPreview && bannerPreview.startsWith("blob:")) {
        URL.revokeObjectURL(bannerPreview);
      }

      setBannerPreview(URL.createObjectURL(file));
      setValue("banner_image", file);
    },
    [bannerPreview, setValue, validateImageSize]
  );

  const handleSliderImagesChange = useCallback(
    (event: React.ChangeEvent<HTMLInputElement>) => {
      const files = Array.from(event.target.files ?? []);
      if (!files.length) return;

      const activeCount = extraImages.filter(
        (image) => image.status !== 0
      ).length;
      const remaining = 6 - activeCount;
      if (remaining <= 0) {
        toast.error({
          title: FIX_PACKAGES_ADMIN.LIMIT_REACHED,
          description: FIX_PACKAGES_ADMIN.MAX_SLIDER_IMAGES,
        });
        event.target.value = "";
        return;
      }

      const imagesToAdd = files.slice(0, remaining).filter(validateImageSize);
      if (!imagesToAdd.length) {
        event.target.value = "";
        return;
      }

      const nextImages = [
        ...extraImages,
        ...imagesToAdd.map((file) => ({ image: file })),
      ];
      setExtraImages(nextImages);
      setValue("images", nextImages);
      setValue("add_more_images", true);
      event.target.value = "";
    },
    [extraImages, setValue, toast, validateImageSize]
  );

  const handleEditImage = useCallback(
    (index: number, file: File) => {
      if (!validateImageSize(file)) return;
      const visibleImages = extraImages.filter((image) => image.status !== 0);
      const target = visibleImages[index];
      if (!target) return;
      const actualIndex = extraImages.indexOf(target);
      if (actualIndex === -1) return;
      const updatedImages = extraImages.map((image, i) =>
        i === actualIndex ? { ...image, image: file } : image
      );
      setExtraImages(updatedImages);
      setValue("images", updatedImages);
    },
    [extraImages, setValue, validateImageSize]
  );

  if (isPageLoading) {
    return (
      <section className="admin-section">
        <header className="admin-page-header mb-6">
          <div className="flex items-center gap-4">
            <Button
              variant="ghost"
              size="icon"
              onClick={() => router.back()}
              aria-label={FIX_PACKAGES_ADMIN.GO_BACK_ARIA}
              type="button"
            >
              <ArrowLeft className="h-5 w-5" />
            </Button>
            <div>
              <h1 className="admin-page-title">Update Package</h1>
              <p className="admin-page-description">
                Loading package details{hasSeedCities ? "" : " and tab data"}...
              </p>
            </div>
          </div>
        </header>
      </section>
    );
  }

  return (
    <section className="admin-section">
      <header className="admin-page-header mb-6">
        <div className="flex items-center gap-4">
          <Button
            variant="ghost"
            size="icon"
            onClick={() => router.back()}
            aria-label={FIX_PACKAGES_ADMIN.GO_BACK_ARIA}
            type="button"
          >
            <ArrowLeft className="h-5 w-5" />
          </Button>
          <div>
            <h1 className="admin-page-title">Update Package</h1>
            <p className="admin-page-description">
              Update package details in grouped tabs with separate save flows.
            </p>
          </div>
        </div>
      </header>

      <Tabs
        value={activeTab}
        onValueChange={(value) => setActiveTab(value as UpdateTab)}
        className="w-full space-y-6"
      >
        <TabsList className="h-auto w-full justify-start gap-2 rounded-xl border border-border/50 bg-primary/10 p-1.5">
          <TabsTrigger value="overview" className="rounded-lg px-4 py-2">
            Package Details
          </TabsTrigger>
          <TabsTrigger value="slider" className="rounded-lg px-4 py-2">
            Slider Images
          </TabsTrigger>
          <TabsTrigger value="itinerary" className="rounded-lg px-4 py-2">
            Itinerary
          </TabsTrigger>
          <TabsTrigger value="extras" className="rounded-lg px-4 py-2">
            Extras
          </TabsTrigger>
        </TabsList>

        <TabsContent value="overview" className="mt-0 space-y-6">
          <BannerImageCard
            bannerPreview={bannerPreview}
            onBannerChange={handleBannerChange}
          />

          <PackageInfoCard
            register={register}
            errors={errors}
            watch={watch}
            setValue={setValue}
            isDataLoaded
          />

          <OtherServicesCard
            availableServices={availableServices}
            servicesLoading={servicesLoading}
            watch={watch}
            setValue={setValue}
          />

          <TagsCard
            availableTags={availableTags}
            tagsLoading={tagsLoading}
            selectedTagIds={selectedTagIds}
            setSelectedTagIds={(value) => {
              const nextValue =
                typeof value === "function" ? value(selectedTagIds) : value;
              setSelectedTagIds(nextValue);
              setValue("tag_ids", nextValue);
            }}
            setValue={setValue}
            onAddTagClick={() => setIsAddTagModalOpen(true)}
          />

          <TabFooter
            isSaving={savingSection === "overview"}
            onCancel={() => router.back()}
            onSave={saveOverview}
            saveLabel="Save Package Details"
          />
        </TabsContent>

        <TabsContent value="slider" className="mt-0 space-y-6">
          <SliderImagesCard
            extraImages={extraImages.filter((image) => image.status !== 0)}
            onSliderImagesChange={handleSliderImagesChange}
            onDeleteImage={handleDeleteImage}
            onEditImage={handleEditImage}
          />

          <TabFooter
            isSaving={savingSection === "slider"}
            onCancel={() => router.back()}
            onSave={saveSliderImages}
            saveLabel="Save Slider Images"
          />
        </TabsContent>

        <TabsContent value="itinerary" className="mt-0 space-y-6">
          <ItineraryDetailsCard
            dayEntries={dayEntries}
            setDayEntries={setDayEntries}
            updateDay={updateDay}
            makeDefaultDay={makeDefaultDay}
            collapsedDayIds={collapsedDayIds}
            setCollapsedDayIds={setCollapsedDayIds}
            errors={errors.itineraries}
            citiesLoading={citiesLoading && !hasSeedCities}
            onOpenVehicleDialog={(dayId, index) => {
              setVehicleTargetDayId(dayId);
              setEditingVehicleIndex(index);
              setIsVehicleDialogOpen(true);
            }}
            onOpenHotelDialog={(dayId, index) => {
              setHotelTargetDayId(dayId);
              setEditingHotelIndex(index);
              setIsHotelDialogOpen(true);
            }}
            onOpenSightseeingModal={(dayId, draft) => {
              setSightseeingTargetId(dayId);
              setSightseeingDraft(draft);
              setIsSightseeingModalOpen(true);
            }}
            itineraryRefs={itineraryRefs}
            onSaveDay={saveItineraryDay}
            savingDayId={savingItineraryDayId}
            onDeleteDay={handleDeleteDay}
            deletingDayId={deletingItineraryDayId}
          />
        </TabsContent>

        <TabsContent value="extras" className="mt-0 space-y-6">
          <ExtrasCard
            extrasEntries={extrasEntries}
            onAddEntry={() =>
              setExtrasEntries((prev) => [
                ...prev,
                { id: createLocalId(), title: "", description: "" },
              ])
            }
            onUpdateEntry={updateExtras}
            onRemoveEntry={removeExtras}
            errors={errors}
            isDataLoaded
            extrasRefs={extrasRefs}
          />

          <TabFooter
            isSaving={savingSection === "extras"}
            onCancel={() => router.back()}
            onSave={saveExtras}
            saveLabel="Save Extras"
          />
        </TabsContent>
      </Tabs>

      <VehicleDialog
        open={isVehicleDialogOpen}
        onOpenChange={setIsVehicleDialogOpen}
        onSave={
          editingVehicleIndex !== null ? handleEditVehicle : handleAddVehicle
        }
        defaultValues={
          editingVehicleIndex !== null && vehicleTargetDayId !== null
            ? dayEntries.find((entry) => entry.id === vehicleTargetDayId)
                ?.vehicles[editingVehicleIndex]
            : undefined
        }
      />

      <HotelDialog
        open={isHotelDialogOpen}
        onOpenChange={setIsHotelDialogOpen}
        onSave={editingHotelIndex !== null ? handleEditHotel : handleAddHotel}
        defaultValues={
          editingHotelIndex !== null && hotelTargetDayId !== null
            ? dayEntries.find((entry) => entry.id === hotelTargetDayId)?.hotels[
                editingHotelIndex
              ]
            : undefined
        }
      />

      <SightseeingModal
        open={isSightseeingModalOpen}
        onOpenChange={setIsSightseeingModalOpen}
        draft={sightseeingDraft}
        onDraftChange={setSightseeingDraft}
        onSave={(items) => {
          if (sightseeingTargetId === null) return;
          updateDay(sightseeingTargetId, { sightseeings: items });
          setIsSightseeingModalOpen(false);
        }}
      />

      <AddTagModal
        open={isAddTagModalOpen}
        onOpenChange={setIsAddTagModalOpen}
        newTagName={newTagName}
        onNewTagNameChange={setNewTagName}
        isTagSaving={isTagSaving}
        onSave={handleAddTag}
      />
    </section>
  );
}
