import { useMemo } from "react";
import { useFieldArray, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { useQuery } from "@tanstack/react-query";
import { useTranslation } from "react-i18next";
import { ChevronDown, Loader2, Plus, Trash2 } from "lucide-react";
import { fetchIngredientCatalogItems } from "@/api/ingredient-catalog.api";
import { MealCategory } from "@/types/recipe.types";
import { useMealCategoryLabels } from "@/i18n/useMealCategoryLabel";
import {
  countUnlinkedMacroIngredients,
  estimateMacrosFromCatalog,
  isUnlinkedMacroIngredient,
} from "@/utils/recipe-macros.util";
import { ingredientDisplayName } from "@/utils/ingredient-catalog.util";
import type { CreateRecipeInput } from "@/types/recipe-management.types";

const MEAL_CATEGORIES = [
  MealCategory.Breakfast,
  MealCategory.SecondBreakfast,
  MealCategory.Lunch,
  MealCategory.Dinner,
] as const;

const EMPTY_DEFAULT: CreateRecipeInput = {
  name: "",
  mealCategory: MealCategory.Breakfast,
  calories: null,
  protein: null,
  fat: null,
  carbs: null,
  prepTimeMinutes: null,
  ingredients: [{ name: "", amountGrams: null, unit: "", sortOrder: 0, catalogItemId: null }],
  steps: [{ stepNumber: 1, description: "" }],
};

interface RecipeFormProps {
  onSubmit: (data: CreateRecipeInput) => Promise<void>;
  pending?: boolean;
  error?: string | null;
  initialValues?: CreateRecipeInput;
  submitLabel?: string;
  resetOnSubmit?: boolean;
}

export function RecipeForm({
  onSubmit,
  pending,
  error,
  initialValues,
  submitLabel,
  resetOnSubmit = true,
}: RecipeFormProps) {
  const { t, i18n } = useTranslation();
  const categoryLabels = useMealCategoryLabels();

  const catalogQuery = useQuery({
    queryKey: ["ingredient-catalog", "all"],
    queryFn: () => fetchIngredientCatalogItems(),
  });

  const recipeSchema = useMemo(
    () =>
      z.object({
        name: z.string().min(1, t("validation.recipeNameRequired")).max(255),
        mealCategory: z.nativeEnum(MealCategory),
        calories: z.coerce.number().int().min(0).nullable().optional(),
        protein: z.coerce.number().min(0).nullable().optional(),
        fat: z.coerce.number().min(0).nullable().optional(),
        carbs: z.coerce.number().min(0).nullable().optional(),
        prepTimeMinutes: z.coerce.number().int().min(0).nullable().optional(),
        ingredients: z
          .array(
            z.object({
              name: z.string().min(1, t("validation.ingredientNameRequired")),
              amountGrams: z.coerce.number().min(0).nullable().optional(),
              unit: z.string().optional(),
              sortOrder: z.coerce.number().int().min(0).default(0),
              catalogItemId: z.coerce.number().nullable().optional(),
            }),
          )
          .default([]),
        steps: z
          .array(
            z.object({
              stepNumber: z.coerce.number().int().min(1, t("validation.stepNumberMin")),
              description: z.string().min(1, t("validation.stepDescriptionRequired")),
            }),
          )
          .min(1, t("validation.stepsMin")),
      }),
    [t],
  );

  type RecipeFormValues = z.infer<typeof recipeSchema>;

  const formDefaults = initialValues ?? EMPTY_DEFAULT;

  const {
    register,
    control,
    handleSubmit,
    reset,
    watch,
    formState: { errors },
  } = useForm<RecipeFormValues>({
    resolver: zodResolver(recipeSchema),
    defaultValues: formDefaults,
  });

  const {
    fields: ingredientFields,
    append: appendIngredient,
    remove: removeIngredient,
  } = useFieldArray({ control, name: "ingredients" });

  const {
    fields: stepFields,
    append: appendStep,
    remove: removeStep,
  } = useFieldArray({ control, name: "steps" });

  const watchedIngredients = watch("ingredients");
  const catalogItems = catalogQuery.data ?? [];

  const estimatedMacros = useMemo(
    () => estimateMacrosFromCatalog(watchedIngredients ?? [], catalogItems),
    [watchedIngredients, catalogItems],
  );

  const unlinkedCount = useMemo(
    () => countUnlinkedMacroIngredients(watchedIngredients ?? []),
    [watchedIngredients],
  );

  const submit = handleSubmit(async (values) => {
    await onSubmit({
      name: values.name,
      mealCategory: values.mealCategory,
      calories: values.calories ?? null,
      protein: values.protein ?? null,
      fat: values.fat ?? null,
      carbs: values.carbs ?? null,
      prepTimeMinutes: values.prepTimeMinutes ?? null,
      ingredients: values.ingredients.map((i, index) => ({
        name: i.name,
        amountGrams: i.amountGrams ?? null,
        unit: i.unit ?? "",
        sortOrder: i.sortOrder ?? index,
        catalogItemId: i.catalogItemId ?? null,
      })),
      steps: values.steps,
    });
    if (resetOnSubmit) reset(EMPTY_DEFAULT);
  });

  return (
    <form onSubmit={submit} className="space-y-8" noValidate>
      {error && (
        <div role="alert" className="rounded-xl bg-red-50 p-3 text-sm text-red-700 dark:bg-red-950/40 dark:text-red-300">
          {error}
        </div>
      )}

      <section className="card space-y-4 p-6">
        <h2 className="text-lg font-bold">{t("manage.basicInfo")}</h2>
        <div className="grid gap-4 sm:grid-cols-2">
          <div className="sm:col-span-2">
            <label className="mb-1.5 block text-sm font-medium">{t("manage.recipeName")}</label>
            <input className="input" {...register("name")} placeholder={t("manage.recipeNamePlaceholder")} />
            {errors.name && <p className="mt-1 text-sm text-red-600">{errors.name.message}</p>}
          </div>
          <div>
            <label className="mb-1.5 block text-sm font-medium">{t("common.category")}</label>
            <div className="relative">
              <select className="select" {...register("mealCategory", { valueAsNumber: true })}>
                {MEAL_CATEGORIES.map((cat) => (
                  <option key={cat} value={cat}>
                    {categoryLabels[cat]}
                  </option>
                ))}
              </select>
              <ChevronDown
                className="pointer-events-none absolute right-3 top-1/2 h-4 w-4 -translate-y-1/2 text-slate-400"
                aria-hidden="true"
              />
            </div>
          </div>
          <div>
            <label className="mb-1.5 block text-sm font-medium">{t("manage.prepTimeMin")}</label>
            <input type="number" className="input" {...register("prepTimeMinutes")} />
          </div>
          <div>
            <label className="mb-1.5 block text-sm font-medium">{t("manage.caloriesKcal")}</label>
            <input type="number" className="input" {...register("calories")} />
          </div>
          <div>
            <label className="mb-1.5 block text-sm font-medium">{t("manage.proteinG")}</label>
            <input type="number" step="0.1" className="input" {...register("protein")} />
          </div>
          <div>
            <label className="mb-1.5 block text-sm font-medium">{t("manage.fatG")}</label>
            <input type="number" step="0.1" className="input" {...register("fat")} />
          </div>
          <div>
            <label className="mb-1.5 block text-sm font-medium">{t("manage.carbsG")}</label>
            <input type="number" step="0.1" className="input" {...register("carbs")} />
          </div>
        </div>
        {estimatedMacros ? (
          <p className="text-sm text-slate-500">
            {t("manage.estimatedFromCatalog")}: {estimatedMacros.calories} kcal · P{" "}
            {estimatedMacros.protein.toFixed(1)}g · F {estimatedMacros.fat.toFixed(1)}g · C{" "}
            {estimatedMacros.carbs.toFixed(1)}g
          </p>
        ) : null}
        {unlinkedCount > 0 ? (
          <p className="text-sm text-amber-700 dark:text-amber-300">
            {t("manage.unlinkedIngredientsWarning", { count: unlinkedCount })}
          </p>
        ) : null}
      </section>

      <section className="card space-y-4 p-6">
        <div className="flex items-center justify-between">
          <h2 className="text-lg font-bold">{t("recipes.ingredients")}</h2>
          <button
            type="button"
            className="btn-secondary"
            onClick={() =>
              appendIngredient({
                name: "",
                amountGrams: null,
                unit: "",
                sortOrder: ingredientFields.length,
                catalogItemId: null,
              })
            }
          >
            <Plus className="h-4 w-4" /> {t("common.add")}
          </button>
        </div>
        <div className="space-y-3">
          {ingredientFields.map((field, index) => {
            const ing = watchedIngredients?.[index];
            const unlinked = ing ? isUnlinkedMacroIngredient(ing) : false;
            return (
            <div
              key={field.id}
              className={`grid gap-2 sm:grid-cols-[1fr_100px_100px_1fr_auto] rounded-lg p-1 ${
                unlinked ? "ring-1 ring-amber-400/70 bg-amber-50/50 dark:bg-amber-950/20" : ""
              }`}
            >
              <input className="input" placeholder={t("manage.ingredientName")} {...register(`ingredients.${index}.name`)} />
              <input type="number" step="0.1" className="input" placeholder={t("manage.ingredientGrams")} {...register(`ingredients.${index}.amountGrams`)} />
              <input className="input" placeholder={t("manage.ingredientUnit")} {...register(`ingredients.${index}.unit`)} />
              <select className="select" {...register(`ingredients.${index}.catalogItemId`, { setValueAs: (v) => (v === "" ? null : Number(v)) })}>
                <option value="">{t("manage.catalogLink")}</option>
                {catalogItems.map((item) => (
                  <option key={item.id} value={item.id}>
                    {ingredientDisplayName(item, i18n.language)}
                  </option>
                ))}
              </select>
              <button type="button" className="btn-secondary !px-2.5" onClick={() => removeIngredient(index)} aria-label={t("manage.removeIngredient")}>
                <Trash2 className="h-4 w-4" />
              </button>
            </div>
            );
          })}
        </div>
      </section>

      <section className="card space-y-4 p-6">
        <div className="flex items-center justify-between">
          <h2 className="text-lg font-bold">{t("manage.preparationSteps")}</h2>
          <button
            type="button"
            className="btn-secondary"
            onClick={() => appendStep({ stepNumber: stepFields.length + 1, description: "" })}
          >
            <Plus className="h-4 w-4" /> {t("manage.addStep")}
          </button>
        </div>
        {errors.steps?.message && <p className="text-sm text-red-600">{errors.steps.message}</p>}
        <div className="space-y-3">
          {stepFields.map((field, index) => (
            <div key={field.id} className="flex gap-2">
              <input type="number" className="input w-20 shrink-0" {...register(`steps.${index}.stepNumber`)} />
              <input className="input flex-1" placeholder={t("manage.stepPlaceholder")} {...register(`steps.${index}.description`)} />
              <button type="button" className="btn-secondary !px-2.5" onClick={() => removeStep(index)} aria-label={t("manage.removeStep")}>
                <Trash2 className="h-4 w-4" />
              </button>
            </div>
          ))}
        </div>
      </section>

      <button type="submit" disabled={pending} className="btn-primary">
        {pending && <Loader2 className="h-4 w-4 animate-spin" />}
        {pending ? t("manage.saving") : (submitLabel ?? t("manage.saveRecipe"))}
      </button>
    </form>
  );
}
