import { useMemo, useState } from "react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useTranslation } from "react-i18next";
import {
  deleteMealPlanEntry,
  fetchMealPlanEntries,
  upsertMealPlanEntry,
} from "@/api/meal-plan.api";
import { fetchRecipes } from "@/api/recipes.api";
import { fetchGoals } from "@/api/diet.api";
import { extractApiError } from "@/api/axiosInstance";
import { ErrorState } from "@/components/ui/ErrorState";
import { Skeleton } from "@/components/ui/Skeleton";
import { MealPlanDayPdfExport } from "@/components/pdf/MealPlanDayPdfExport";
import type { MealPlanEntry } from "@/types/meal-plan.types";
import { MealCategory } from "@/types/recipe.types";
import { useMealCategoryLabels } from "@/i18n/useMealCategoryLabel";
import {
  computeDayMealPlanning,
  entryCaloriesTotal,
  estimateEntryCalories,
  rankRecipeSuggestions,
} from "@/utils/mealPlannerSuggestions.util";
import {
  addDaysIso,
  endOfWeekIso,
  formatDisplayDate,
  startOfWeekIso,
  todayIso,
} from "@/utils/date.util";

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

export function MealPlannerPage() {
  const { t, i18n } = useTranslation();
  const queryClient = useQueryClient();
  const categoryLabels = useMealCategoryLabels();
  const [weekStart, setWeekStart] = useState(() => startOfWeekIso(todayIso()));
  const [adding, setAdding] = useState<{ date: string; category: number } | null>(null);
  const [recipeSearch, setRecipeSearch] = useState("");
  const [selectedRecipeId, setSelectedRecipeId] = useState<number | null>(null);
  const [portions, setPortions] = useState(1);
  const [dailyTargets, setDailyTargets] = useState<Record<string, number>>({});

  const weekEnd = endOfWeekIso(weekStart);
  const weekDays = useMemo(
    () =>
      Array.from({ length: 7 }, (_, i) => {
        const iso = addDaysIso(weekStart, i);
        return { iso, label: formatDisplayDate(iso, i18n.language) };
      }),
    [weekStart, i18n.language],
  );

  const entriesQuery = useQuery({
    queryKey: ["meal-plan", weekStart, weekEnd],
    queryFn: () => fetchMealPlanEntries(weekStart, weekEnd),
  });

  const goalsQuery = useQuery({
    queryKey: ["diet-goals"],
    queryFn: fetchGoals,
  });

  const isSearching = recipeSearch.trim().length >= 2;

  const recipesQuery = useQuery({
    queryKey: ["recipes", "planner-search", recipeSearch],
    queryFn: () => fetchRecipes({ search: recipeSearch }),
    enabled: !!adding && isSearching,
  });

  const suggestionsQuery = useQuery({
    queryKey: ["recipes", "planner-suggestions", adding?.category],
    queryFn: () => fetchRecipes({ category: adding!.category as MealCategory }),
    enabled: !!adding,
  });

  const saveMutation = useMutation({
    mutationFn: upsertMealPlanEntry,
    onSuccess: () => {
      void queryClient.invalidateQueries({ queryKey: ["meal-plan"] });
      setAdding(null);
      setRecipeSearch("");
      setSelectedRecipeId(null);
      setPortions(1);
    },
  });

  const deleteMutation = useMutation({
    mutationFn: deleteMealPlanEntry,
    onSuccess: () => void queryClient.invalidateQueries({ queryKey: ["meal-plan"] }),
  });

  const entries = entriesQuery.data ?? [];

  const dailyTarget = adding
    ? (dailyTargets[adding.date] ?? goalsQuery.data?.calorieGoal ?? 0)
    : 0;

  const selectedRecipe = useMemo(() => {
    if (!selectedRecipeId) return null;
    return (
      recipesQuery.data?.find((r) => r.id === selectedRecipeId) ??
      suggestionsQuery.data?.find((r) => r.id === selectedRecipeId) ??
      null
    );
  }, [selectedRecipeId, recipesQuery.data, suggestionsQuery.data]);

  const estimatedMealCalories = estimateEntryCalories(selectedRecipe, portions);

  const dayPlanning = useMemo(() => {
    if (!adding) return null;
    const pending =
      estimatedMealCalories != null && estimatedMealCalories > 0
        ? { category: adding.category, calories: estimatedMealCalories }
        : null;
    return computeDayMealPlanning(adding.date, adding.category, entries, dailyTarget, pending);
  }, [adding, entries, dailyTarget, estimatedMealCalories]);

  const suggestions = useMemo(() => {
    if (!adding || !dayPlanning || !suggestionsQuery.data) return [];
    return rankRecipeSuggestions(suggestionsQuery.data, adding.category, dayPlanning.slotTarget);
  }, [adding, dayPlanning, suggestionsQuery.data]);

  function selectFromSearch(recipeId: number) {
    setSelectedRecipeId(recipeId);
    setRecipeSearch("");
  }

  function selectFromSuggestion(recipeId: number) {
    setSelectedRecipeId(recipeId);
  }

  function clearSelection() {
    setSelectedRecipeId(null);
  }

  function entriesFor(date: string, category: number): MealPlanEntry[] {
    return entries.filter((e) => e.planDate === date && e.mealCategory === category);
  }

  async function saveEntry() {
    if (!adding || !selectedRecipeId) return;
    await saveMutation.mutateAsync({
      planDate: adding.date,
      mealCategory: adding.category,
      recipeId: selectedRecipeId,
      portions,
    });
  }

  return (
    <div className="space-y-6">
      <header>
        <h1 className="text-2xl font-extrabold tracking-tight sm:text-3xl">{t("mealPlanner.title")}</h1>
        <p className="mt-1 text-slate-500 dark:text-slate-400">{t("mealPlanner.subtitle")}</p>
      </header>

      <div className="flex flex-wrap items-center gap-2">
        <button type="button" className="btn-secondary !py-1.5 !text-sm" onClick={() => setWeekStart(addDaysIso(weekStart, -7))}>
          ←
        </button>
        <button type="button" className="btn-secondary !py-1.5 !text-sm" onClick={() => setWeekStart(startOfWeekIso(todayIso()))}>
          {t("mealPlanner.today")}
        </button>
        <button type="button" className="btn-secondary !py-1.5 !text-sm" onClick={() => setWeekStart(addDaysIso(weekStart, 7))}>
          →
        </button>
        <span className="text-sm font-medium text-slate-600 dark:text-slate-300">
          {formatDisplayDate(weekStart, i18n.language)} – {formatDisplayDate(weekEnd, i18n.language)}
        </span>
      </div>

      {entriesQuery.isLoading ? (
        <Skeleton className="h-64" />
      ) : entriesQuery.isError ? (
        <ErrorState
          message={extractApiError(entriesQuery.error, t("errors.loadFailed"))}
          onRetry={() => void entriesQuery.refetch()}
        />
      ) : (
        <div className="overflow-x-auto">
          <table className="w-full min-w-[640px] border-collapse text-sm">
            <thead>
              <tr>
                <th className="p-2 text-left font-semibold text-slate-500">{t("common.category")}</th>
                {weekDays.map((day) => (
                  <th
                    key={day.iso}
                    className={`p-2 text-left font-semibold ${day.iso === todayIso() ? "text-brand-600" : ""}`}
                  >
                    <div className="flex flex-col gap-1">
                      <span>{day.label}</span>
                      <MealPlanDayPdfExport date={day.iso} entries={entries} />
                    </div>
                  </th>
                ))}
              </tr>
            </thead>
            <tbody>
              {PLANNER_CATEGORIES.map((cat) => (
                <tr key={cat} className="border-t border-slate-200 dark:border-slate-800">
                  <td className="p-2 font-medium">{categoryLabels[cat]}</td>
                  {weekDays.map((day) => (
                    <td key={day.iso} className="p-2 align-top">
                      {entriesFor(day.iso, cat).map((entry) => (
                        <div
                          key={entry.id}
                          className="mb-1 flex items-start justify-between gap-1 rounded-lg bg-slate-50 px-2 py-1 dark:bg-slate-800/50"
                        >
                          <div>
                            <p className="font-medium">{entry.recipeName}</p>
                            <p className="text-xs text-slate-400">
                              {entry.portions}× · {entryCaloriesTotal(entry) || "?"} kcal
                            </p>
                          </div>
                          <button
                            type="button"
                            className="text-slate-400 hover:text-red-500"
                            onClick={() => void deleteMutation.mutateAsync(entry.id)}
                          >
                            ✕
                          </button>
                        </div>
                      ))}
                      <button
                        type="button"
                        className="mt-1 text-xs font-semibold text-brand-600 hover:underline"
                        onClick={() => {
                          setAdding({ date: day.iso, category: cat });
                          setRecipeSearch("");
                          setSelectedRecipeId(null);
                        }}
                      >
                        + {t("mealPlanner.addRecipe")}
                      </button>
                    </td>
                  ))}
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}

      {adding ? (
        <div className="card space-y-4 p-5">
          <h2 className="text-lg font-bold">{t("mealPlanner.addRecipe")}</h2>
          <p className="text-sm text-slate-500">
            {formatDisplayDate(adding.date, i18n.language)} · {categoryLabels[adding.category as MealCategory]}
          </p>
          <label className="block text-sm">
            <span className="font-medium">{t("mealPlanner.dailyTarget")}</span>
            <input
              type="number"
              min={0}
              step={50}
              className="input mt-1 w-full"
              value={dailyTarget || ""}
              onChange={(e) => {
                const value = Number(e.target.value);
                setDailyTargets((prev) => ({
                  ...prev,
                  [adding.date]: Number.isFinite(value) ? value : 0,
                }));
              }}
            />
          </label>
          {dayPlanning && dailyTarget > 0 ? (
            <div className="rounded-xl bg-slate-50 px-3 py-2 text-sm text-slate-600 dark:bg-slate-800/50 dark:text-slate-300">
              <p>
                {t("mealPlanner.daySummary", {
                  logged: dayPlanning.logged,
                  target: dayPlanning.dailyTarget,
                })}
              </p>
              <p className="text-xs text-slate-500 dark:text-slate-400">
                {t("mealPlanner.remaining", { count: Math.max(0, dayPlanning.remaining) })}
                {dayPlanning.slotTarget > 0
                  ? ` · ${t("mealPlanner.slotHint", { count: dayPlanning.slotTarget })}`
                  : null}
                {dayPlanning.pendingCalories > 0
                  ? ` · ${t("mealPlanner.includingSelection", { count: dayPlanning.pendingCalories })}`
                  : null}
              </p>
            </div>
          ) : (
            <p className="text-xs text-slate-500">{t("mealPlanner.noSuggestions")}</p>
          )}
          {suggestions.length > 0 ? (
            <div>
              <p className="mb-2 text-sm font-medium">{t("mealPlanner.suggestionsTitle")}</p>
              <ul className="max-h-48 space-y-1 overflow-y-auto rounded-xl border border-brand-200 bg-brand-50/50 p-2 dark:border-brand-900 dark:bg-brand-950/30">
                {suggestions.map(({ recipe, diff }) => (
                  <li key={recipe.id}>
                    <button
                      type="button"
                      onClick={() => selectFromSuggestion(recipe.id)}
                      className={`w-full rounded-lg px-3 py-2 text-left text-sm ${
                        selectedRecipeId === recipe.id
                          ? "ring-2 ring-brand-600 ring-inset"
                          : "hover:bg-white dark:hover:bg-slate-800"
                      }`}
                    >
                      <span className="font-medium">{recipe.name}</span>
                      <span className="ml-2 text-xs text-slate-500 dark:text-slate-400">
                        {recipe.calories} kcal
                        {diff > 0 ? ` · ±${diff}` : ""}
                      </span>
                    </button>
                  </li>
                ))}
              </ul>
            </div>
          ) : suggestionsQuery.isLoading ? (
            <Skeleton className="h-24" />
          ) : null}
          <label className="block text-sm">
            <span className="font-medium">{t("diet.searchRecipe")}</span>
            <input
              className="input mt-1 w-full"
              value={recipeSearch}
              onChange={(e) => setRecipeSearch(e.target.value)}
            />
          </label>
          {isSearching && !selectedRecipeId && recipesQuery.isLoading ? (
            <Skeleton className="h-16" />
          ) : null}
          {isSearching && !selectedRecipeId && recipesQuery.data && recipesQuery.data.length > 0 ? (
            <ul className="max-h-40 space-y-1 overflow-y-auto rounded-xl border border-slate-200 p-2 dark:border-slate-700">
              {recipesQuery.data.map((r) => (
                <li key={r.id}>
                  <button
                    type="button"
                    onClick={() => selectFromSearch(r.id)}
                    className="w-full rounded-lg px-3 py-2 text-left text-sm hover:bg-slate-100 dark:hover:bg-slate-800"
                  >
                    {r.name}
                    {r.calories != null ? (
                      <span className="ml-2 text-xs text-slate-500">{r.calories} kcal</span>
                    ) : null}
                  </button>
                </li>
              ))}
            </ul>
          ) : isSearching && !selectedRecipeId && recipesQuery.isFetched && !recipesQuery.isLoading ? (
            <p className="text-xs text-slate-500">{t("recipes.noMatchingTitle")}</p>
          ) : null}
          {selectedRecipe ? (
            <div className="flex items-center justify-between gap-2 rounded-xl border border-brand-300 bg-brand-50 px-3 py-2 text-sm dark:border-brand-800 dark:bg-brand-950/40">
              <span className="font-medium text-brand-900 dark:text-brand-100">
                {t("mealPlanner.selectedRecipe", { name: selectedRecipe.name })}
                {estimatedMealCalories != null
                  ? ` · ${estimatedMealCalories} kcal`
                  : selectedRecipe.calories != null
                    ? ` · ${selectedRecipe.calories} kcal`
                    : ""}
              </span>
              <button
                type="button"
                className="text-slate-400 hover:text-red-500"
                onClick={clearSelection}
                aria-label={t("mealPlanner.clearSelection")}
              >
                ✕
              </button>
            </div>
          ) : null}
          <label className="block text-sm">
            <span className="font-medium">{t("diet.portions")}</span>
            <input
              type="number"
              min={0.25}
              step={0.25}
              className="input mt-1 w-full"
              value={portions}
              onChange={(e) => setPortions(Number(e.target.value))}
            />
          </label>
          {estimatedMealCalories != null ? (
            <p className="text-sm text-slate-500">
              {t("mealPlanner.estimatedMeal", { count: estimatedMealCalories })}
            </p>
          ) : null}
          <div className="flex gap-2">
            <button
              type="button"
              className="btn-primary"
              disabled={!selectedRecipeId || saveMutation.isPending}
              onClick={() => void saveEntry()}
            >
              {t("common.save")}
            </button>
            <button type="button" className="btn-secondary" onClick={() => setAdding(null)}>
              {t("common.cancel")}
            </button>
          </div>
        </div>
      ) : null}
    </div>
  );
}
