"use client";

import { useState, useRef, useEffect, KeyboardEvent } from "react";
import { useRouter } from "next/navigation";
import { Plus, Trash2, GripVertical, X, Tag } from "lucide-react";
import { toast } from "sonner";
import { useTranslations } from "next-intl";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { Label } from "@/components/ui/label";
import { Separator } from "@/components/ui/separator";
import { Switch } from "@/components/ui/switch";
import { cn } from "@/lib/utils";
import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
} from "@/components/ui/alert-dialog";
import { DietaryTagPicker } from "./dietary-tag-picker";
import { PhotoUploader, type PhotoEntry } from "./photo-uploader";
import { RecipeCoverPicker } from "./recipe-cover-picker";
import { RegenerateRecipeButton } from "./regenerate-recipe-button";
import type { RegeneratedRecipe } from "@/lib/ai/features/regenerate-recipe";

type DietaryTags = {
  vegan?: boolean;
  vegetarian?: boolean;
  glutenFree?: boolean;
  dairyFree?: boolean;
  nutFree?: boolean;
  halal?: boolean;
  kosher?: boolean;
};

type IngredientRow = {
  id: string;
  rawName: string;
  quantity: string;
  unit: string;
  note: string;
};

type TimerUnit = "seconds" | "minutes" | "hours";

type StepRow = {
  id: string;
  instruction: string;
  timerSeconds: string;
  timerUnit: TimerUnit;
  appliesTo: string[];
};

const TIMER_UNIT_SECONDS: Record<TimerUnit, number> = { seconds: 1, minutes: 60, hours: 3600 };

/** Picks the largest unit that divides evenly into the stored seconds, so
 *  editing a 90-minute braise shows "90 min" rather than "5400 sec". */
function secondsToTimerInput(totalSeconds: number): { value: string; unit: TimerUnit } {
  if (totalSeconds > 0 && totalSeconds % 3600 === 0) return { value: String(totalSeconds / 3600), unit: "hours" };
  if (totalSeconds > 0 && totalSeconds % 60 === 0) return { value: String(totalSeconds / 60), unit: "minutes" };
  return { value: String(totalSeconds), unit: "seconds" };
}

type DishRow = {
  id: string;
  name: string;
  description: string;
  fridgeDays: string;
  freezerFriendly: boolean;
  freezerNote: string;
  dayOfInstructions: string;
};

type RecipeFormProps = {
  recipeId?: string;
  defaultValues?: {
    title?: string;
    description?: string;
    baseServings?: number;
    recipeType?: "dish" | "drink";
    visibility?: "private" | "unlisted" | "public" | "followers";
    difficulty?: "easy" | "medium" | "hard" | null;
    prepMins?: number | null;
    cookMins?: number | null;
    dietaryTags?: DietaryTags;
    tags?: string[];
    ingredients?: IngredientRow[];
    steps?: StepRow[];
    photos?: PhotoEntry[];
    coverIcon?: string | null;
    coverColor?: string | null;
    isBatchCook?: boolean;
    dishes?: DishRow[];
    nutrition?: {
      calories: number;
      proteinG: number;
      carbsG: number;
      fatG: number;
      fiberG: number;
      sodiumMg: number;
    } | null;
  };
};

type NutritionValues = {
  calories: string;
  proteinG: string;
  carbsG: string;
  fatG: string;
  fiberG: string;
  sodiumMg: string;
};

const EMPTY_NUTRITION: NutritionValues = { calories: "", proteinG: "", carbsG: "", fatG: "", fiberG: "", sodiumMg: "" };

function newIngredient(): IngredientRow {
  return { id: crypto.randomUUID(), rawName: "", quantity: "", unit: "", note: "" };
}

function newStep(): StepRow {
  return { id: crypto.randomUUID(), instruction: "", timerSeconds: "", timerUnit: "minutes", appliesTo: [] };
}

function newDish(): DishRow {
  return { id: crypto.randomUUID(), name: "", description: "", fridgeDays: "3", freezerFriendly: false, freezerNote: "", dayOfInstructions: "" };
}

export function RecipeForm({ recipeId, defaultValues }: RecipeFormProps) {
  const router = useRouter();
  const t = useTranslations("recipeForm");
  const t_recipe = useTranslations("recipe");
  const t_common = useTranslations("common");
  const t_nutrition = useTranslations("nutritionPanel");
  const isEdit = !!recipeId;
  const id = recipeId ?? crypto.randomUUID();

  const [title, setTitle] = useState(defaultValues?.title ?? "");
  const [description, setDescription] = useState(defaultValues?.description ?? "");
  const [baseServings, setBaseServings] = useState(String(defaultValues?.baseServings ?? 4));
  const [recipeType, setRecipeType] = useState<"dish" | "drink">(defaultValues?.recipeType ?? "dish");
  const [visibility, setVisibility] = useState<"private" | "unlisted" | "public" | "followers">(defaultValues?.visibility ?? "private");
  const [difficulty, setDifficulty] = useState<"easy" | "medium" | "hard" | "">(defaultValues?.difficulty ?? "");
  const [prepMins, setPrepMins] = useState(String(defaultValues?.prepMins ?? ""));
  const [cookMins, setCookMins] = useState(String(defaultValues?.cookMins ?? ""));
  const [tags, setTags] = useState<string[]>(defaultValues?.tags ?? []);
  const [tagInput, setTagInput] = useState("");
  const tagInputRef = useRef<HTMLInputElement>(null);
  const [dietaryTags, setDietaryTags] = useState<DietaryTags>(defaultValues?.dietaryTags ?? {});
  const [addNutrition, setAddNutrition] = useState(!!defaultValues?.nutrition);
  const [nutrition, setNutrition] = useState<NutritionValues>(
    defaultValues?.nutrition
      ? {
          calories: String(defaultValues.nutrition.calories),
          proteinG: String(defaultValues.nutrition.proteinG),
          carbsG: String(defaultValues.nutrition.carbsG),
          fatG: String(defaultValues.nutrition.fatG),
          fiberG: String(defaultValues.nutrition.fiberG),
          sodiumMg: String(defaultValues.nutrition.sodiumMg),
        }
      : EMPTY_NUTRITION
  );
  const [ingredients, setIngredients] = useState<IngredientRow[]>(
    defaultValues?.ingredients?.length ? defaultValues.ingredients : [newIngredient()]
  );
  const [steps, setSteps] = useState<StepRow[]>(
    defaultValues?.steps?.length ? defaultValues.steps : [newStep()]
  );
  const [photos, setPhotos] = useState<PhotoEntry[]>(defaultValues?.photos ?? []);
  const [coverIcon, setCoverIcon] = useState<string | null>(defaultValues?.coverIcon ?? null);
  const [coverColor, setCoverColor] = useState<string | null>(defaultValues?.coverColor ?? null);
  const [isBatchCook, setIsBatchCook] = useState(defaultValues?.isBatchCook ?? false);
  const [dishes, setDishes] = useState<DishRow[]>(
    defaultValues?.dishes?.length ? defaultValues.dishes : [newDish()]
  );
  const [saving, setSaving] = useState(false);
  const [dirty, setDirty] = useState(false);
  const [discardConfirmOpen, setDiscardConfirmOpen] = useState(false);
  const hasMountedRef = useRef(false);

  // Mark the form dirty on any edit after the initial mount, so we can warn the
  // user before they navigate away and silently lose their changes.
  useEffect(() => {
    if (!hasMountedRef.current) {
      hasMountedRef.current = true;
      return;
    }
    setDirty(true);
  }, [
    title,
    description,
    baseServings,
    visibility,
    difficulty,
    prepMins,
    cookMins,
    tags,
    dietaryTags,
    ingredients,
    steps,
    photos,
    isBatchCook,
    dishes,
    recipeType,
    addNutrition,
    nutrition,
  ]);

  // Browser-level guard: warn on tab close / reload / external navigation while dirty.
  useEffect(() => {
    if (!dirty || saving) return;
    function handleBeforeUnload(e: BeforeUnloadEvent) {
      e.preventDefault();
      e.returnValue = "";
    }
    window.addEventListener("beforeunload", handleBeforeUnload);
    return () => window.removeEventListener("beforeunload", handleBeforeUnload);
  }, [dirty, saving]);

  function handleCancelClick() {
    if (dirty) {
      setDiscardConfirmOpen(true);
    } else {
      router.back();
    }
  }

  function updateIngredient(i: number, patch: Partial<IngredientRow>) {
    setIngredients((prev) => prev.map((row, idx) => idx === i ? { ...row, ...patch } : row));
  }

  function removeIngredient(i: number) {
    setIngredients((prev) => prev.filter((_, idx) => idx !== i));
  }

  // Merges an AI-regenerated draft into the in-progress form state — it
  // never touches the DB directly, so the user still has to hit Save.
  function applyRegenerated(recipe: RegeneratedRecipe) {
    setTitle(recipe.title);
    if (recipe.description !== undefined) setDescription(recipe.description);
    setBaseServings(String(recipe.baseServings));
    if (recipe.difficulty) setDifficulty(recipe.difficulty);
    setPrepMins(recipe.prepMins !== undefined ? String(recipe.prepMins) : "");
    setCookMins(recipe.cookMins !== undefined ? String(recipe.cookMins) : "");
    if (recipe.dietaryTags) setDietaryTags(recipe.dietaryTags);
    setIngredients(recipe.ingredients.map((ing) => ({
      id: crypto.randomUUID(),
      rawName: ing.rawName,
      quantity: ing.quantity !== undefined ? String(ing.quantity) : "",
      unit: ing.unit ?? "",
      note: ing.note ?? "",
    })));
    setSteps(recipe.steps.map((step) => {
      const timer = step.timerSeconds !== undefined ? secondsToTimerInput(step.timerSeconds) : null;
      return {
        id: crypto.randomUUID(),
        instruction: step.instruction,
        timerSeconds: timer?.value ?? "",
        timerUnit: timer?.unit ?? "minutes",
        appliesTo: [],
      };
    }));
  }

  function addTag(raw: string) {
    const tag = raw.trim().toLowerCase().slice(0, 50);
    if (!tag || tags.includes(tag) || tags.length >= 20) return;
    setTags((prev) => [...prev, tag]);
    setTagInput("");
  }

  function removeTag(tag: string) {
    setTags((prev) => prev.filter((t) => t !== tag));
  }

  function handleTagKeyDown(e: KeyboardEvent<HTMLInputElement>) {
    if (e.key === "Enter") {
      e.preventDefault();
      addTag(tagInput);
    } else if (e.key === "Backspace" && !tagInput && tags.length > 0) {
      setTags((prev) => prev.slice(0, -1));
    }
  }

  function updateStep(i: number, patch: Partial<StepRow>) {
    setSteps((prev) => prev.map((row, idx) => idx === i ? { ...row, ...patch } : row));
  }

  function removeStep(i: number) {
    setSteps((prev) => prev.filter((_, idx) => idx !== i));
  }

  function toggleStepDish(i: number, dishName: string) {
    setSteps((prev) => prev.map((row, idx) => {
      if (idx !== i) return row;
      const has = row.appliesTo.includes(dishName);
      return { ...row, appliesTo: has ? row.appliesTo.filter((n) => n !== dishName) : [...row.appliesTo, dishName] };
    }));
  }

  function updateDish(i: number, patch: Partial<DishRow>) {
    const oldName = dishes[i]?.name;
    setDishes((prev) => prev.map((row, idx) => idx === i ? { ...row, ...patch } : row));
    // appliesTo tracks dishes by name (matches the DB's join-by-string-array
    // design), so a rename must be propagated or steps silently detach.
    if (patch.name !== undefined && oldName && patch.name !== oldName) {
      setSteps((prev) => prev.map((row) => ({
        ...row,
        appliesTo: row.appliesTo.map((n) => n === oldName ? patch.name! : n),
      })));
    }
  }

  function removeDish(i: number) {
    const removedName = dishes[i]?.name;
    setDishes((prev) => prev.filter((_, idx) => idx !== i));
    if (removedName) {
      setSteps((prev) => prev.map((row) => ({ ...row, appliesTo: row.appliesTo.filter((n) => n !== removedName) })));
    }
  }

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    if (!title.trim()) {
      toast.error(t("titleRequired"));
      return;
    }

    const filteredIngredients = ingredients
      .filter((ing) => ing.rawName.trim())
      .map((ing, i) => ({
        rawName: ing.rawName.trim(),
        quantity: ing.quantity.trim() || undefined,
        unit: ing.unit.trim() || undefined,
        note: ing.note.trim() || undefined,
        order: i,
      }));

    if (filteredIngredients.length === 0) {
      toast.error(t("ingredientsRequired"));
      return;
    }

    const filteredDishes = isBatchCook
      ? dishes
          .filter((d) => d.name.trim())
          .map((d) => ({
            name: d.name.trim(),
            description: d.description.trim() || undefined,
            fridgeDays: parseInt(d.fridgeDays) || 3,
            freezerFriendly: d.freezerFriendly,
            freezerNote: d.freezerNote.trim() || undefined,
            dayOfInstructions: d.dayOfInstructions.trim(),
          }))
      : [];

    if (isBatchCook && filteredDishes.length === 0) {
      toast.error(t("dishesRequired"));
      return;
    }
    if (isBatchCook && filteredDishes.some((d) => !d.dayOfInstructions)) {
      toast.error(t("dayOfInstructionsRequired"));
      return;
    }

    const dishNames = new Set(filteredDishes.map((d) => d.name));
    const filteredSteps = steps
      .filter((s) => s.instruction.trim())
      .map((s, i) => ({
        instruction: s.instruction.trim(),
        timerSeconds: s.timerSeconds ? parseInt(s.timerSeconds) * TIMER_UNIT_SECONDS[s.timerUnit] : undefined,
        order: i,
        appliesTo: isBatchCook ? s.appliesTo.filter((n) => dishNames.has(n)) : [],
      }));

    if (filteredSteps.length === 0) {
      toast.error(t("stepsRequired"));
      return;
    }

    setSaving(true);
    try {
      const payload = {
        title: title.trim(),
        description: description.trim() || undefined,
        baseServings: parseInt(baseServings) || 4,
        recipeType,
        visibility,
        difficulty: difficulty || undefined,
        prepMins: prepMins ? parseInt(prepMins) : undefined,
        cookMins: cookMins ? parseInt(cookMins) : undefined,
        tags,
        dietaryTags,
        ingredients: filteredIngredients,
        steps: filteredSteps,
        photos: photos.map((p) => ({ key: p.key, isCover: p.isCover })),
        coverIcon,
        coverColor,
        isBatchCook,
        dishes: filteredDishes,
        nutrition: addNutrition
          ? {
              calories: Number(nutrition.calories) || 0,
              proteinG: Number(nutrition.proteinG) || 0,
              carbsG: Number(nutrition.carbsG) || 0,
              fatG: Number(nutrition.fatG) || 0,
              fiberG: Number(nutrition.fiberG) || 0,
              sodiumMg: Number(nutrition.sodiumMg) || 0,
            }
          : isEdit ? null : undefined,
      };

      const url = isEdit ? `/api/v1/recipes/${id}` : "/api/v1/recipes";
      const method = isEdit ? "PUT" : "POST";

      const res = await fetch(url, {
        method,
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(payload),
      });

      if (!res.ok) {
        const err = await res.json() as { error?: string };
        toast.error(err.error ?? t("saveError"));
        return;
      }

      const saved = await res.json() as { id: string };
      toast.success(isEdit ? t("updateSuccess") : t("createSuccess"));
      setDirty(false);
      router.push(`/recipes/${saved.id}`);
      router.refresh();
    } finally {
      setSaving(false);
    }
  }

  return (
    <form onSubmit={handleSubmit} className="space-y-8 max-w-3xl pb-20">
      {/* Basic info */}
      <div className="space-y-4">
        {isEdit && (
          <div className="flex justify-end">
            <RegenerateRecipeButton
              current={{
                title,
                description,
                baseServings: Number(baseServings) || 1,
                difficulty,
                ingredients: ingredients.map((ing) => ({ rawName: ing.rawName, quantity: ing.quantity, unit: ing.unit })),
                steps: steps.map((step) => ({ instruction: step.instruction })),
              }}
              onRegenerated={applyRegenerated}
            />
          </div>
        )}
        <div className="space-y-2">
          <Label htmlFor="title">{t("titleLabel")}</Label>
          <Input
            id="title"
            value={title}
            onChange={(e) => setTitle(e.target.value)}
            placeholder={t("titlePlaceholder")}
            required
          />
        </div>

        <div className="space-y-2">
          <Label htmlFor="description">{t("descriptionLabel")}</Label>
          <Textarea
            id="description"
            value={description}
            onChange={(e) => setDescription(e.target.value)}
            placeholder={t("descriptionPlaceholder")}
            rows={3}
          />
        </div>

        <div className="space-y-2">
          <Label htmlFor="recipeType">{t("recipeType")}</Label>
          <select
            id="recipeType"
            value={recipeType}
            onChange={(e) => setRecipeType(e.target.value as "dish" | "drink")}
            className="flex h-8 w-full max-w-[200px] rounded-lg border border-input bg-transparent px-2.5 py-1 text-sm outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50"
          >
            <option value="dish">{t("recipeTypeDish")}</option>
            <option value="drink">{t("recipeTypeDrink")}</option>
          </select>
        </div>

        <div className={cn("grid grid-cols-2 gap-4", recipeType === "dish" && "md:grid-cols-4")}>
          <div className="space-y-2">
            <Label htmlFor="servings">{recipeType === "drink" ? t("servingsDrink") : t("servings")}</Label>
            <Input
              id="servings"
              type="number"
              min={1}
              max={100}
              value={baseServings}
              onChange={(e) => setBaseServings(e.target.value)}
            />
          </div>
          {recipeType === "dish" && (
            <>
              <div className="space-y-2">
                <Label htmlFor="prep">{t("prepMins")}</Label>
                <Input
                  id="prep"
                  type="number"
                  min={0}
                  value={prepMins}
                  onChange={(e) => setPrepMins(e.target.value)}
                  placeholder="0"
                />
              </div>
              <div className="space-y-2">
                <Label htmlFor="cook">{t("cookMins")}</Label>
                <Input
                  id="cook"
                  type="number"
                  min={0}
                  value={cookMins}
                  onChange={(e) => setCookMins(e.target.value)}
                  placeholder="0"
                />
              </div>
              <div className="space-y-2">
                <Label htmlFor="difficulty">{t("difficulty")}</Label>
                <select
                  id="difficulty"
                  value={difficulty}
                  onChange={(e) => setDifficulty(e.target.value as "easy" | "medium" | "hard" | "")}
                  className="flex h-8 w-full rounded-lg border border-input bg-transparent px-2.5 py-1 text-sm outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50"
                >
                  <option value="">—</option>
                  <option value="easy">{t_recipe("difficulty.easy")}</option>
                  <option value="medium">{t_recipe("difficulty.medium")}</option>
                  <option value="hard">{t_recipe("difficulty.hard")}</option>
                </select>
              </div>
            </>
          )}
        </div>

        <div className="space-y-2">
          <Label htmlFor="visibility">{t_recipe("visibilityMenuLabel")}</Label>
          <select
            id="visibility"
            value={visibility}
            onChange={(e) => setVisibility(e.target.value as "private" | "unlisted" | "public" | "followers")}
            className="flex h-8 w-full max-w-[200px] rounded-lg border border-input bg-transparent px-2.5 py-1 text-sm outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50"
          >
            <option value="private">{t_recipe("visibility.private")}</option>
            <option value="followers">{t_recipe("visibility.followers")}</option>
            <option value="unlisted">{t_recipe("visibility.unlisted")}</option>
            <option value="public">{t_recipe("visibility.public")}</option>
          </select>
        </div>

        {/* Tags */}
        <div className="space-y-2">
          <Label>{t("tags")}</Label>
          <div
            className="flex flex-wrap gap-1.5 min-h-[36px] rounded-lg border border-input bg-transparent px-2.5 py-1.5 cursor-text focus-within:border-ring focus-within:ring-3 focus-within:ring-ring/50"
            onClick={() => tagInputRef.current?.focus()}
          >
            {tags.map((tag) => (
              <span
                key={tag}
                className="inline-flex items-center gap-1 px-2 py-0.5 rounded-md text-xs bg-muted text-muted-foreground"
              >
                <Tag className="h-2.5 w-2.5" />
                {tag}
                <button
                  type="button"
                  onClick={(e) => { e.stopPropagation(); removeTag(tag); }}
                  className="hover:text-foreground transition-colors"
                  aria-label={t("removeTagAriaLabel", { tag })}
                >
                  <X className="h-2.5 w-2.5" />
                </button>
              </span>
            ))}
            {tags.length < 20 && (
              <input
                ref={tagInputRef}
                value={tagInput}
                onChange={(e) => setTagInput(e.target.value)}
                onKeyDown={handleTagKeyDown}
                onBlur={() => { if (tagInput.trim()) addTag(tagInput); }}
                placeholder={tags.length === 0 ? t("tagsPlaceholder") : ""}
                className="flex-1 min-w-[120px] bg-transparent text-sm outline-none placeholder:text-muted-foreground"
              />
            )}
          </div>
          <p className="text-xs text-muted-foreground">{t("tagsHelp")}</p>
        </div>
      </div>

      <Separator />

      {/* Dietary tags */}
      <div className="space-y-2">
        <Label>{t("dietaryTags")}</Label>
        <DietaryTagPicker value={dietaryTags} onChange={setDietaryTags} />
      </div>

      <Separator />

      {/* Manual nutrition */}
      <div className="space-y-3">
        <div className="flex items-center gap-3">
          <Switch id="add-nutrition" checked={addNutrition} onCheckedChange={setAddNutrition} />
          <Label htmlFor="add-nutrition" className="cursor-pointer">{t("nutritionToggle")}</Label>
        </div>
        <p className="text-xs text-muted-foreground">{t("nutritionToggleHelp")}</p>
        {addNutrition && (
          <div className="grid grid-cols-2 md:grid-cols-3 gap-4">
            <div className="space-y-2">
              <Label htmlFor="nutrition-calories">{t("calories")}</Label>
              <Input
                id="nutrition-calories"
                type="number"
                min={0}
                value={nutrition.calories}
                onChange={(e) => setNutrition((prev) => ({ ...prev, calories: e.target.value }))}
              />
            </div>
            <div className="space-y-2">
              <Label htmlFor="nutrition-protein">{t_nutrition("protein")} (g)</Label>
              <Input
                id="nutrition-protein"
                type="number"
                min={0}
                value={nutrition.proteinG}
                onChange={(e) => setNutrition((prev) => ({ ...prev, proteinG: e.target.value }))}
              />
            </div>
            <div className="space-y-2">
              <Label htmlFor="nutrition-carbs">{t_nutrition("carbs")} (g)</Label>
              <Input
                id="nutrition-carbs"
                type="number"
                min={0}
                value={nutrition.carbsG}
                onChange={(e) => setNutrition((prev) => ({ ...prev, carbsG: e.target.value }))}
              />
            </div>
            <div className="space-y-2">
              <Label htmlFor="nutrition-fat">{t_nutrition("fat")} (g)</Label>
              <Input
                id="nutrition-fat"
                type="number"
                min={0}
                value={nutrition.fatG}
                onChange={(e) => setNutrition((prev) => ({ ...prev, fatG: e.target.value }))}
              />
            </div>
            <div className="space-y-2">
              <Label htmlFor="nutrition-fiber">{t_nutrition("fiber")} (g)</Label>
              <Input
                id="nutrition-fiber"
                type="number"
                min={0}
                value={nutrition.fiberG}
                onChange={(e) => setNutrition((prev) => ({ ...prev, fiberG: e.target.value }))}
              />
            </div>
            <div className="space-y-2">
              <Label htmlFor="nutrition-sodium">{t_nutrition("sodium")} (mg)</Label>
              <Input
                id="nutrition-sodium"
                type="number"
                min={0}
                value={nutrition.sodiumMg}
                onChange={(e) => setNutrition((prev) => ({ ...prev, sodiumMg: e.target.value }))}
              />
            </div>
          </div>
        )}
      </div>

      {recipeType === "dish" && (
        <>
          <Separator />

          {/* Batch cooking */}
          <div className="space-y-2">
            <div className="flex items-center gap-3">
              <Switch id="batch-cook" checked={isBatchCook} onCheckedChange={setIsBatchCook} />
              <Label htmlFor="batch-cook" className="cursor-pointer">{t("batchCookToggle")}</Label>
            </div>
            <p className="text-xs text-muted-foreground">{t("batchCookToggleHelp")}</p>
          </div>
        </>
      )}

      <Separator />

      {/* Photos */}
      <div className="space-y-2">
        <Label>{t("photos")}</Label>
        <PhotoUploader recipeId={id} photos={photos} onChange={setPhotos} />
      </div>

      {/* Cover placeholder (used when there's no photo above) */}
      <div className="space-y-2">
        <Label>{t("cover")}</Label>
        <RecipeCoverPicker
          recipeType={recipeType}
          color={coverColor}
          icon={coverIcon}
          onChange={({ color, icon }) => {
            setCoverColor(color);
            setCoverIcon(icon);
          }}
        />
      </div>

      <Separator />

      {/* Ingredients */}
      <div className="space-y-3">
        <Label>{t("ingredients")}</Label>
        <div className="space-y-2">
          {ingredients.map((ing, i) => (
            <div key={ing.id} className="flex flex-col sm:flex-row gap-2 sm:items-start">
              <div className="flex gap-2 items-start">
                <GripVertical className="h-4 w-4 text-muted-foreground mt-2 cursor-grab shrink-0" />
                <Input
                  value={ing.quantity}
                  onChange={(e) => updateIngredient(i, { quantity: e.target.value })}
                  placeholder={t("amount")}
                  className="w-16 shrink-0"
                />
                <Input
                  value={ing.unit}
                  onChange={(e) => updateIngredient(i, { unit: e.target.value })}
                  placeholder={t("unit")}
                  className="w-16 sm:w-24 shrink-0"
                />
                <Input
                  value={ing.rawName}
                  onChange={(e) => updateIngredient(i, { rawName: e.target.value })}
                  placeholder={t("ingredient")}
                  className="flex-1 min-w-0"
                />
              </div>
              <div className="flex gap-2 items-start pl-6 sm:pl-0">
                <Input
                  value={ing.note}
                  onChange={(e) => updateIngredient(i, { note: e.target.value })}
                  placeholder={t("note")}
                  className="flex-1 sm:w-48 sm:shrink-0"
                />
                <button
                  type="button"
                  onClick={() => removeIngredient(i)}
                  disabled={ingredients.length === 1}
                  className="mt-2 text-muted-foreground hover:text-destructive transition-colors disabled:opacity-30 shrink-0"
                >
                  <Trash2 className="h-4 w-4" />
                </button>
              </div>
            </div>
          ))}
        </div>
        <Button
          type="button"
          variant="outline"
          size="sm"
          onClick={() => setIngredients((p) => [...p, newIngredient()])}
        >
          <Plus className="h-4 w-4" />
          {t("addIngredient")}
        </Button>
      </div>

      <Separator />

      {/* Batch-cook dishes */}
      {isBatchCook && (
        <>
          <div className="space-y-3">
            <Label>{t("dishes")}</Label>
            <div className="space-y-4">
              {dishes.map((dish, i) => (
                <div key={dish.id} className="rounded-lg border p-3 space-y-2">
                  <div className="flex gap-2 items-start">
                    <Input
                      value={dish.name}
                      onChange={(e) => updateDish(i, { name: e.target.value })}
                      placeholder={t("dishName")}
                      className="flex-1 min-w-0 font-medium"
                    />
                    <button
                      type="button"
                      onClick={() => removeDish(i)}
                      disabled={dishes.length === 1}
                      className="mt-1.5 text-muted-foreground hover:text-destructive transition-colors disabled:opacity-30"
                    >
                      <Trash2 className="h-4 w-4" />
                    </button>
                  </div>
                  <Textarea
                    value={dish.description}
                    onChange={(e) => updateDish(i, { description: e.target.value })}
                    placeholder={t("dishDescription")}
                    rows={2}
                  />
                  <div className="grid grid-cols-2 md:grid-cols-4 gap-2 items-end">
                    <div className="space-y-1">
                      <Label htmlFor={`fridge-${dish.id}`} className="text-xs text-muted-foreground">{t("fridgeDays")}</Label>
                      <Input
                        id={`fridge-${dish.id}`}
                        type="number"
                        min={1}
                        max={14}
                        value={dish.fridgeDays}
                        onChange={(e) => updateDish(i, { fridgeDays: e.target.value })}
                      />
                    </div>
                    <div className="flex items-center gap-2 pb-1.5">
                      <Switch
                        id={`freezer-${dish.id}`}
                        checked={dish.freezerFriendly}
                        onCheckedChange={(v) => updateDish(i, { freezerFriendly: v })}
                      />
                      <Label htmlFor={`freezer-${dish.id}`} className="cursor-pointer text-xs">{t("freezerFriendly")}</Label>
                    </div>
                    {dish.freezerFriendly && (
                      <div className="space-y-1 col-span-2">
                        <Label htmlFor={`freezer-note-${dish.id}`} className="text-xs text-muted-foreground">{t("freezerNote")}</Label>
                        <Input
                          id={`freezer-note-${dish.id}`}
                          value={dish.freezerNote}
                          onChange={(e) => updateDish(i, { freezerNote: e.target.value })}
                        />
                      </div>
                    )}
                  </div>
                  <div className="space-y-1">
                    <Label htmlFor={`dayof-${dish.id}`} className="text-xs text-muted-foreground">{t("dayOfInstructions")}</Label>
                    <Textarea
                      id={`dayof-${dish.id}`}
                      value={dish.dayOfInstructions}
                      onChange={(e) => updateDish(i, { dayOfInstructions: e.target.value })}
                      rows={2}
                    />
                  </div>
                </div>
              ))}
            </div>
            <Button
              type="button"
              variant="outline"
              size="sm"
              onClick={() => setDishes((p) => [...p, newDish()])}
            >
              <Plus className="h-4 w-4" />
              {t("addDish")}
            </Button>
          </div>

          <Separator />
        </>
      )}

      {/* Steps */}
      <div className="space-y-3">
        <Label>{t("steps")}</Label>
        <div className="space-y-3">
          {steps.map((step, i) => (
            <div key={step.id} className="space-y-1.5">
              <div className="flex flex-col sm:flex-row gap-2 sm:gap-3">
                <div className="flex gap-3 flex-1 min-w-0">
                  <div className="mt-2.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-muted text-xs font-bold text-muted-foreground">
                    {i + 1}
                  </div>
                  <Textarea
                    value={step.instruction}
                    onChange={(e) => updateStep(i, { instruction: e.target.value })}
                    placeholder={t("stepPlaceholder", { n: i + 1 })}
                    rows={2}
                    className="flex-1 min-w-0"
                  />
                </div>
                <div className="flex gap-2 items-start pl-9 sm:pl-0">
                  <Input
                    value={step.timerSeconds}
                    onChange={(e) => updateStep(i, { timerSeconds: e.target.value })}
                    placeholder={t("timerSeconds")}
                    type="number"
                    min={0}
                    className="w-20 shrink-0"
                  />
                  <select
                    value={step.timerUnit}
                    onChange={(e) => updateStep(i, { timerUnit: e.target.value as TimerUnit })}
                    aria-label={t("timerUnitAriaLabel")}
                    className="h-8 shrink-0 rounded-lg border border-input bg-transparent px-2 text-sm outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50"
                  >
                    <option value="seconds">{t("timerUnit.seconds")}</option>
                    <option value="minutes">{t("timerUnit.minutes")}</option>
                    <option value="hours">{t("timerUnit.hours")}</option>
                  </select>
                  <button
                    type="button"
                    onClick={() => removeStep(i)}
                    disabled={steps.length === 1}
                    className="mt-2 text-muted-foreground hover:text-destructive transition-colors disabled:opacity-30 shrink-0"
                  >
                    <Trash2 className="h-4 w-4" />
                  </button>
                </div>
              </div>
              {isBatchCook && dishes.some((d) => d.name.trim()) && (
                <div className="flex flex-wrap gap-1.5 pl-9">
                  <span className="text-xs text-muted-foreground self-center">{t("appliesTo")}:</span>
                  {dishes.filter((d) => d.name.trim()).map((d) => (
                    <button
                      key={d.id}
                      type="button"
                      onClick={() => toggleStepDish(i, d.name.trim())}
                      className={cn(
                        "px-2 py-0.5 rounded-full text-xs border transition-colors",
                        step.appliesTo.includes(d.name.trim())
                          ? "bg-primary text-primary-foreground border-primary"
                          : "bg-transparent text-muted-foreground border-input hover:border-muted-foreground/50"
                      )}
                    >
                      {d.name.trim()}
                    </button>
                  ))}
                  {step.appliesTo.length === 0 && (
                    <span className="text-xs text-muted-foreground italic self-center">{t("sharedPrep")}</span>
                  )}
                </div>
              )}
            </div>
          ))}
        </div>
        <Button
          type="button"
          variant="outline"
          size="sm"
          onClick={() => setSteps((p) => [...p, newStep()])}
        >
          <Plus className="h-4 w-4" />
          {t("addStep")}
        </Button>
      </div>

      <Separator />

      <div className="flex gap-3">
        <Button type="submit" disabled={saving}>
          {saving ? t("saving") : isEdit ? t("saveChanges") : t("createRecipe")}
        </Button>
        <Button type="button" variant="outline" onClick={handleCancelClick}>
          {t_common("cancel")}
        </Button>
      </div>

      {/* Floating save bar — the form above can get long, this stays reachable without scrolling back down */}
      <div className="fixed bottom-4 left-1/2 -translate-x-1/2 z-40 flex items-center gap-2 rounded-full border bg-popover p-1.5 shadow-lg">
        <Button type="submit" size="sm" className="rounded-full" disabled={saving}>
          {saving ? t("saving") : isEdit ? t("saveChanges") : t("createRecipe")}
        </Button>
        <Button type="button" size="sm" variant="ghost" className="rounded-full" onClick={handleCancelClick}>
          {t_common("cancel")}
        </Button>
      </div>

      <AlertDialog open={discardConfirmOpen} onOpenChange={setDiscardConfirmOpen}>
        <AlertDialogContent>
          <AlertDialogHeader>
            <AlertDialogTitle>{t("discardChangesTitle")}</AlertDialogTitle>
            <AlertDialogDescription>{t("discardChangesDescription")}</AlertDialogDescription>
          </AlertDialogHeader>
          <AlertDialogFooter>
            <AlertDialogCancel>{t("keepEditing")}</AlertDialogCancel>
            <AlertDialogAction
              onClick={() => {
                setDiscardConfirmOpen(false);
                setDirty(false);
                router.back();
              }}
              className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
            >
              {t("discardChanges")}
            </AlertDialogAction>
          </AlertDialogFooter>
        </AlertDialogContent>
      </AlertDialog>
    </form>
  );
}
