import { DecimalPipe } from '@angular/common';
import { Component, OnDestroy, OnInit, computed, inject, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { TranslatePipe, TranslateService } from '@ngx-translate/core';
import { Subject, debounceTime, distinctUntilChanged, of, switchMap, takeUntil } from 'rxjs';
import { DietService } from '../../core/services/diet.service';
import { IngredientCatalogService } from '../../core/services/ingredient-catalog.service';
import { LanguageService } from '../../core/services/language.service';
import { RecipeService } from '../../core/services/recipe.service';
import {
  CONSUMPTION_CATEGORIES,
  DailySummary,
  DietReminder,
  DietReminderType,
  DietSuggestion,
  FavoriteCatalogItem,
  FavoriteRecipe,
  HistoryDay,
  LogMealInput,
  MealPreview,
  RecentMeal,
  REMINDER_DAYS_MASK_ALL,
  UserDailyGoals,
  addDaysIso,
  todayIso,
} from '../../core/models/diet.models';
import { IngredientNutritionItem } from '../../core/models/ingredient-catalog.models';
import { RecipeListItem } from '../../core/models/recipe.models';
import { ErrorStateComponent } from '../../shared/components/error-state.component';
import { SkeletonComponent } from '../../shared/components/skeleton.component';
import { extractApiError } from '../../core/utils/api-error.util';
import { formatReminderTime } from '../../core/utils/diet-normalize.util';
import { nextSort, sortBy, sortIndicator, type SortDirection } from '../../core/utils/sort.util';

type MealLogSortColumn = 'name' | 'calories';
type DrinkLogSortColumn = 'name' | 'volume';
type ReminderSortColumn = 'title' | 'time';
type Tab = 'today' | 'goals' | 'reminders' | 'history';
type HistoryRange = 7 | 30;

@Component({
  selector: 'app-diet-tracker',
  standalone: true,
  imports: [DecimalPipe, FormsModule, TranslatePipe, ErrorStateComponent, SkeletonComponent],
  template: `
    <div class="space-y-6">
      <header>
        <h1 class="text-2xl font-extrabold tracking-tight sm:text-3xl">{{ 'diet.title' | translate }}</h1>
        <p class="mt-1 text-slate-500 dark:text-slate-400">{{ 'diet.subtitle' | translate }}</p>
      </header>

      <div class="flex flex-wrap gap-2">
        @for (t of tabs; track t.id) {
          <button type="button" (click)="setTab(t.id)" class="rounded-xl px-4 py-2 text-sm font-semibold transition" [class.bg-brand-600]="tab() === t.id" [class.text-white]="tab() === t.id" [class.bg-slate-100]="tab() !== t.id" [class.text-slate-600]="tab() !== t.id" [class.dark:bg-slate-800]="tab() !== t.id">
            {{ t.labelKey | translate }}
          </button>
        }
      </div>

      @if (tab() === 'today') {
        <div class="flex flex-wrap items-center gap-2">
          <button type="button" class="btn-secondary !py-1.5 !text-sm" (click)="shiftDate(-1)">←</button>
          <button type="button" class="btn-secondary !py-1.5 !text-sm" (click)="goToday()">{{ 'diet.goToday' | translate }}</button>
          <button type="button" class="btn-secondary !py-1.5 !text-sm" (click)="shiftDate(1)">→</button>
          <span class="text-sm font-semibold" [class.text-brand-600]="selectedDate() === todayIso()">{{ selectedDate() }}</span>
          @if (selectedDate() !== todayIso()) {
            <span class="text-xs text-slate-400">({{ 'diet.notToday' | translate }})</span>
          }
        </div>

        @if (loading()) {
          <app-skeleton [className]="'h-48 w-full'" />
        } @else if (error()) {
          <app-error-state [message]="error()!" [showRetry]="true" (retry)="loadSummary()" />
        } @else if (summary()) {
          <section class="card space-y-3 p-5">
            <h2 class="text-lg font-bold">{{ 'diet.quickLog' | translate }}</h2>
            <div class="flex flex-wrap gap-2">
              <button type="button" class="btn-secondary !py-1.5 !text-xs" [disabled]="busy()" (click)="copyYesterday()">
                {{ 'diet.copyYesterday' | translate }}
              </button>
            </div>
            @if (recentMeals().length > 0) {
              <div>
                <p class="text-sm font-medium text-slate-500">{{ 'diet.recentMeals' | translate }}</p>
                <div class="mt-2 flex flex-wrap gap-2">
                  @for (m of recentMeals(); track m.name + m.lastLoggedAt) {
                    <button type="button" class="rounded-xl border border-slate-200 px-3 py-1.5 text-xs font-medium hover:border-brand-400 dark:border-slate-700" [disabled]="busy()" (click)="logRecent(m)">
                      {{ m.name }}
                    </button>
                  }
                </div>
              </div>
            }
            @if (favoriteRecipes().length > 0 || favoriteCatalog().length > 0) {
              <div>
                <p class="text-sm font-medium text-slate-500">{{ 'diet.favorites' | translate }}</p>
                <div class="mt-2 flex flex-wrap gap-2">
                  @for (f of favoriteRecipes(); track f.recipeId) {
                    <button type="button" class="rounded-xl bg-brand-50 px-3 py-1.5 text-xs font-medium text-brand-800 dark:bg-brand-950/30" [disabled]="busy()" (click)="logFavoriteRecipe(f)">
                      ★ {{ f.name }}
                    </button>
                  }
                  @for (f of favoriteCatalog(); track f.catalogItemId) {
                    <button type="button" class="rounded-xl bg-slate-50 px-3 py-1.5 text-xs font-medium dark:bg-slate-800/50" [disabled]="busy()" (click)="logFavoriteCatalog(f)">
                      ★ {{ favoriteCatalogName(f) }}
                    </button>
                  }
                </div>
              </div>
            }
          </section>

          <div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5">
            @for (card of progressCards(); track card.labelKey) {
              <div class="card p-4">
                <p class="text-sm font-medium text-slate-500 dark:text-slate-400">{{ card.labelKey | translate }}</p>
                <p class="mt-1 text-2xl font-bold">
                  {{ card.consumed }}
                  @if (card.goal != null) {
                    <span class="text-base font-normal text-slate-400">/ {{ card.goal }}{{ card.unit }}</span>
                  }
                </p>
                @if (card.remaining != null) {
                  <p class="mt-1 text-xs text-slate-500">{{ 'diet.remaining' | translate }}: {{ card.remaining }}{{ card.unit }}</p>
                }
                @if (card.goal != null) {
                  <div class="mt-3 h-2 overflow-hidden rounded-full bg-slate-100 dark:bg-slate-800">
                    <div class="h-full rounded-full bg-brand-600" [style.width.%]="card.percent"></div>
                  </div>
                }
              </div>
            }
          </div>

          @if (suggestions().length > 0) {
            <section class="card p-5">
              <h2 class="text-lg font-bold">{{ 'diet.suggestionsTitle' | translate }}</h2>
              <p class="mt-1 text-sm text-slate-500">{{ 'diet.suggestionsHint' | translate }}</p>
              <ul class="mt-4 space-y-2">
                @for (s of suggestions(); track s.catalogItemId) {
                  <li class="flex flex-wrap items-center justify-between gap-2 rounded-xl bg-slate-50 px-3 py-2 dark:bg-slate-800/50">
                    <div>
                      <p class="font-medium">{{ suggestionName(s) }}</p>
                      <p class="text-xs text-slate-400">
                        {{ s.suggestedGrams }}g · {{ s.calories ?? '?' }} kcal · P {{ s.proteinG | number: '1.0-1' }}g
                        · {{ ('diet.suggestionReasons.' + s.reasonKey) | translate }}
                      </p>
                    </div>
                    <button type="button" class="btn-secondary !py-1.5 !text-xs" [disabled]="busy()" (click)="logSuggestion(s)">
                      + {{ 'diet.logSuggestion' | translate }}
                    </button>
                  </li>
                }
              </ul>
            </section>
          }

          <section class="card space-y-4 p-5">
            <h2 class="text-lg font-bold">{{ 'diet.logDrink' | translate }}</h2>
            <div class="flex flex-wrap gap-2">
              @for (item of catalog(); track item.id) {
                <button type="button" [disabled]="busy()" (click)="logDrinkFromCatalog(item.id, item.defaultVolumeMl)" class="inline-flex items-center gap-2 rounded-xl border border-slate-200 px-3 py-2 text-sm font-medium hover:border-brand-400 hover:bg-brand-50 dark:border-slate-700">
                  <span>{{ item.iconEmoji ?? '🥤' }}</span>
                  {{ item.name }}
                  <span class="text-slate-400">+{{ item.defaultVolumeMl }}ml</span>
                </button>
              }
            </div>
          </section>

          <section class="card space-y-4 p-5">
            <h2 class="text-lg font-bold">{{ 'diet.logMeal' | translate }}</h2>
            <label class="block text-sm">
              <span class="font-medium">{{ 'common.category' | translate }}</span>
              <select class="select mt-1 w-full" [(ngModel)]="mealCategory">
                @for (c of categories; track c.value) {
                  <option [ngValue]="c.value">{{ c.labelKey | translate }}</option>
                }
              </select>
            </label>
            <label class="block text-sm">
              <span class="font-medium">{{ 'diet.searchRecipe' | translate }}</span>
              <input class="input mt-1 w-full" [(ngModel)]="recipeSearch" (ngModelChange)="onRecipeSearch($event)" />
            </label>
            @if (recipeResults().length > 0) {
              <ul class="max-h-40 space-y-1 overflow-y-auto rounded-xl border border-slate-200 p-2 dark:border-slate-700">
                @for (r of recipeResults(); track r.id) {
                  <li>
                    <button type="button" (click)="selectRecipe(r.id)" class="w-full rounded-lg px-3 py-2 text-left text-sm" [class.bg-brand-600]="selectedRecipeId === r.id" [class.text-white]="selectedRecipeId === r.id">
                      {{ r.name }}
                    </button>
                  </li>
                }
              </ul>
            }
            <div class="grid gap-4 sm:grid-cols-2">
              <label class="block text-sm">
                <span class="font-medium">{{ 'diet.portions' | translate }}</span>
                <input type="number" min="0.25" step="0.25" class="input mt-1 w-full" [(ngModel)]="portions" (ngModelChange)="refreshPreview()" />
              </label>
              <label class="block text-sm">
                <span class="font-medium">{{ 'diet.fromCatalog' | translate }}</span>
                <select class="select mt-1 w-full" [(ngModel)]="catalogItemId" (ngModelChange)="onCatalogItemChange($event)">
                  <option [ngValue]="null">{{ 'diet.noCatalogItem' | translate }}</option>
                  @for (item of ingredientItems(); track item.id) {
                    <option [ngValue]="item.id">{{ displayIngredientName(item) }}</option>
                  }
                </select>
              </label>
            </div>
            @if (catalogItemId) {
              <label class="block text-sm">
                <span class="font-medium">{{ 'diet.grams' | translate }}</span>
                <input type="number" min="1" class="input mt-1 w-full" [(ngModel)]="grams" (ngModelChange)="refreshPreview()" />
              </label>
            }
            <label class="block text-sm">
              <span class="font-medium">{{ 'diet.orCustomMeal' | translate }}</span>
              <input class="input mt-1 w-full" [(ngModel)]="customMealName" (ngModelChange)="onCustomMealChange($event)" />
            </label>

            @if (mealPreview()) {
              <div class="rounded-xl border border-brand-200 bg-brand-50/60 p-4 dark:border-brand-900 dark:bg-brand-950/20">
                <p class="font-semibold">{{ 'diet.mealPreview' | translate }}: {{ mealPreview()!.meal.name }}</p>
                <p class="mt-2 text-sm">
                  {{ mealPreview()!.meal.calories ?? '?' }} kcal ·
                  P {{ (mealPreview()!.meal.proteinG ?? 0) | number: '1.0-1' }}g ·
                  F {{ (mealPreview()!.meal.fatG ?? 0) | number: '1.0-1' }}g ·
                  C {{ (mealPreview()!.meal.carbsG ?? 0) | number: '1.0-1' }}g
                </p>
                <p class="mt-2 text-sm text-slate-600 dark:text-slate-300">
                  {{ 'diet.remainingAfter' | translate }}:
                  {{ mealPreview()!.remainingAfter.calories ?? '?' }} kcal,
                  P {{ mealPreview()!.remainingAfter.proteinG ?? '?' }}g,
                  F {{ mealPreview()!.remainingAfter.fatG ?? '?' }}g,
                  C {{ mealPreview()!.remainingAfter.carbsG ?? '?' }}g
                </p>
              </div>
            }

            <button type="button" class="btn-secondary" [disabled]="busy() || !canLogMeal()" (click)="logMeal()">
              + {{ 'diet.markEaten' | translate }}
            </button>
          </section>

          <div class="grid gap-4 lg:grid-cols-2">
            <div class="card p-5">
              <h3 class="font-bold">{{ mealsHeading() }}</h3>
              @if ((summary()?.meals?.length ?? 0) > 0) {
                <div class="mt-3 grid grid-cols-[1fr_auto_auto] gap-2 border-b border-slate-200 px-3 pb-2 text-xs font-semibold uppercase tracking-wide text-slate-500 dark:border-slate-700">
                  <button type="button" class="text-left hover:text-slate-700" (click)="toggleMealSort('name')">
                    {{ 'ingredientCatalog.columns.name' | translate }}{{ mealSortIndicator('name') }}
                  </button>
                  <button type="button" class="text-right hover:text-slate-700" (click)="toggleMealSort('calories')">
                    {{ 'recipes.calories' | translate }}{{ mealSortIndicator('calories') }}
                  </button>
                  <span></span>
                </div>
              }
              <ul class="mt-3 space-y-2">
                @if ((summary()?.meals?.length ?? 0) === 0) {
                  <li class="text-sm text-slate-400">{{ 'diet.nothingLogged' | translate }}</li>
                } @else {
                  @for (m of sortedMeals(); track m.id) {
                    <li class="flex items-center justify-between rounded-lg bg-slate-50 px-3 py-2 dark:bg-slate-800/50">
                      <div>
                        <p class="font-medium">{{ m.name }}</p>
                        <p class="text-xs text-slate-400">{{ mealMacroLine(m) }}</p>
                      </div>
                      <button type="button" class="text-slate-400 hover:text-red-500" (click)="removeMeal(m.id)">✕</button>
                    </li>
                  }
                }
              </ul>
            </div>
            <div class="card p-5">
              <h3 class="font-bold">{{ drinksHeading() }}</h3>
              @if ((summary()?.drinks?.length ?? 0) > 0) {
                <div class="mt-3 grid grid-cols-[1fr_auto_auto] gap-2 border-b border-slate-200 px-3 pb-2 text-xs font-semibold uppercase tracking-wide text-slate-500 dark:border-slate-700">
                  <button type="button" class="text-left hover:text-slate-700" (click)="toggleDrinkSort('name')">
                    {{ 'ingredientCatalog.columns.name' | translate }}{{ drinkSortIndicator('name') }}
                  </button>
                  <button type="button" class="text-right hover:text-slate-700" (click)="toggleDrinkSort('volume')">
                    {{ 'diet.water' | translate }}{{ drinkSortIndicator('volume') }}
                  </button>
                  <span></span>
                </div>
              }
              <ul class="mt-3 space-y-2">
                @if ((summary()?.drinks?.length ?? 0) === 0) {
                  <li class="text-sm text-slate-400">{{ 'diet.nothingLogged' | translate }}</li>
                } @else {
                  @for (d of sortedDrinks(); track d.id) {
                    <li class="flex items-center justify-between rounded-lg bg-slate-50 px-3 py-2 dark:bg-slate-800/50">
                      <div>
                        <p class="font-medium">{{ d.name }}</p>
                        <p class="text-xs text-slate-400">{{ d.volumeMl }} ml</p>
                      </div>
                      <button type="button" class="text-slate-400 hover:text-red-500" (click)="removeDrink(d.id)">✕</button>
                    </li>
                  }
                }
              </ul>
            </div>
          </div>
        }
      }

      @if (tab() === 'history') {
        <div class="flex gap-2">
          <button type="button" class="rounded-xl px-4 py-2 text-sm font-semibold" [class.bg-brand-600]="historyRange() === 7" [class.text-white]="historyRange() === 7" [class.bg-slate-100]="historyRange() !== 7" (click)="setHistoryRange(7)">7 {{ 'diet.days' | translate }}</button>
          <button type="button" class="rounded-xl px-4 py-2 text-sm font-semibold" [class.bg-brand-600]="historyRange() === 30" [class.text-white]="historyRange() === 30" [class.bg-slate-100]="historyRange() !== 30" (click)="setHistoryRange(30)">30 {{ 'diet.days' | translate }}</button>
        </div>
        @if (historyLoading()) {
          <app-skeleton [className]="'h-48 w-full'" />
        } @else if (history().length === 0) {
          <p class="text-sm text-slate-400">{{ 'diet.noHistory' | translate }}</p>
        } @else {
          <div class="card p-5">
            <h2 class="text-lg font-bold">{{ 'diet.historyCalories' | translate }}</h2>
            <div class="mt-6 flex items-end gap-1 overflow-x-auto pb-2" [style.height.px]="180">
              @for (day of history(); track day.date) {
                <div class="flex min-w-[24px] flex-1 flex-col items-center justify-end gap-1">
                  <div
                    class="w-full max-w-[32px] rounded-t bg-brand-600 transition-all"
                    [style.height.%]="barHeight(day.calories)"
                    [title]="day.date + ': ' + day.calories + ' kcal'"
                  ></div>
                  <span class="text-[10px] text-slate-400">{{ dayLabel(day.date) }}</span>
                </div>
              }
            </div>
            <p class="mt-2 text-xs text-slate-400">{{ 'diet.historyMax' | translate: { max: historyMaxCalories() } }}</p>
          </div>
        }
      }

      @if (tab() === 'goals') {
        <div class="card max-w-lg space-y-4 p-5">
          <h2 class="text-lg font-bold">{{ 'diet.dailyGoals' | translate }}</h2>
          <p class="text-sm text-slate-500">{{ 'diet.goalsHint' | translate }}</p>
          @for (field of goalFields; track field.key) {
            <label class="block text-sm">
              <span class="font-medium">{{ field.labelKey | translate }}</span>
              <input type="number" min="0" class="input mt-1 w-full" [(ngModel)]="goalsForm[field.key]" />
            </label>
          }
          <button type="button" class="btn-secondary" [disabled]="busy()" (click)="saveGoals()">{{ 'diet.saveGoals' | translate }}</button>
        </div>
      }

      @if (tab() === 'reminders') {
        <div class="grid gap-6 lg:grid-cols-2">
          <div class="card space-y-4 p-5">
            <h2 class="text-lg font-bold">{{ editingReminderId() ? ('diet.editReminder' | translate) : ('diet.addReminder' | translate) }}</h2>
            <label class="block text-sm">
              <span class="font-medium">{{ 'diet.reminderType' | translate }}</span>
              <select class="select mt-1 w-full" [(ngModel)]="reminderType">
                <option [ngValue]="reminderTypes.Water">{{ 'diet.reminderWater' | translate }}</option>
                <option [ngValue]="reminderTypes.Meal">{{ 'diet.reminderMeal' | translate }}</option>
                <option [ngValue]="reminderTypes.Custom">{{ 'diet.reminderCustom' | translate }}</option>
              </select>
            </label>
            <label class="block text-sm">
              <span class="font-medium">{{ 'diet.reminderTitle' | translate }}</span>
              <input class="input mt-1 w-full" [(ngModel)]="reminderTitle" />
            </label>
            <label class="block text-sm">
              <span class="font-medium">{{ 'diet.reminderTime' | translate }}</span>
              <input type="time" class="input mt-1 w-full" [(ngModel)]="reminderTime" />
            </label>
            <div class="flex gap-2">
              <button type="button" class="btn-secondary" [disabled]="busy() || !reminderTitle.trim()" (click)="saveReminder()">
                {{ editingReminderId() ? ('common.save' | translate) : ('diet.saveReminder' | translate) }}
              </button>
              @if (editingReminderId()) {
                <button type="button" class="btn-secondary" (click)="cancelEditReminder()">{{ 'common.cancel' | translate }}</button>
              }
            </div>
          </div>
          <div class="card p-5">
            <h2 class="text-lg font-bold">{{ 'diet.yourReminders' | translate }}</h2>
            @if (reminders().length > 0) {
              <div class="mt-3 grid grid-cols-[1fr_auto_auto_auto] gap-2 border-b border-slate-200 px-3 pb-2 text-xs font-semibold uppercase tracking-wide text-slate-500 dark:border-slate-700">
                <button type="button" class="text-left hover:text-slate-700" (click)="toggleReminderSort('title')">
                  {{ 'diet.reminderTitle' | translate }}{{ reminderSortIndicator('title') }}
                </button>
                <button type="button" class="text-right hover:text-slate-700" (click)="toggleReminderSort('time')">
                  {{ 'diet.reminderTime' | translate }}{{ reminderSortIndicator('time') }}
                </button>
                <span></span><span></span>
              </div>
            }
            <ul class="mt-3 space-y-2">
              @if (reminders().length === 0) {
                <li class="text-sm text-slate-400">{{ 'diet.noReminders' | translate }}</li>
              } @else {
                @for (r of sortedReminders(); track r.id) {
                  <li class="flex items-center justify-between rounded-lg bg-slate-50 px-3 py-2 dark:bg-slate-800/50">
                    <div>
                      <p class="font-medium">{{ r.title }}</p>
                      <p class="text-xs text-slate-400">{{ formatReminderTime(r.timeOfDay) }}</p>
                    </div>
                    <div class="flex gap-2">
                      <button type="button" class="text-slate-400 hover:text-brand-600" (click)="startEditReminder(r)">{{ 'common.edit' | translate }}</button>
                      <button type="button" class="text-slate-400 hover:text-red-500" (click)="removeReminder(r.id)">✕</button>
                    </div>
                  </li>
                }
              }
            </ul>
          </div>
        </div>
      }
    </div>
  `,
})
export class DietTrackerComponent implements OnInit, OnDestroy {
  private readonly diet = inject(DietService);
  private readonly recipes = inject(RecipeService);
  private readonly ingredients = inject(IngredientCatalogService);
  private readonly translate = inject(TranslateService);
  private readonly language = inject(LanguageService);
  private readonly destroy$ = new Subject<void>();
  private readonly recipeSearch$ = new Subject<string>();
  private readonly preview$ = new Subject<void>();

  readonly categories = CONSUMPTION_CATEGORIES;
  readonly reminderTypes = DietReminderType;
  readonly todayIso = todayIso;
  readonly tabs: { id: Tab; labelKey: string }[] = [
    { id: 'today', labelKey: 'diet.tabToday' },
    { id: 'history', labelKey: 'diet.tabHistory' },
    { id: 'goals', labelKey: 'diet.tabGoals' },
    { id: 'reminders', labelKey: 'diet.tabReminders' },
  ];
  readonly goalFields: { key: keyof UserDailyGoals; labelKey: string }[] = [
    { key: 'calorieGoal', labelKey: 'diet.calorieGoal' },
    { key: 'proteinGoalG', labelKey: 'manage.proteinG' },
    { key: 'fatGoalG', labelKey: 'manage.fatG' },
    { key: 'carbsGoalG', labelKey: 'manage.carbsG' },
    { key: 'waterGoalMl', labelKey: 'diet.waterGoal' },
  ];

  tab = signal<Tab>('today');
  selectedDate = signal(todayIso());
  loading = signal(false);
  busy = signal(false);
  error = signal<string | null>(null);
  summary = signal<DailySummary | null>(null);
  catalog = signal<{ id: number; name: string; defaultVolumeMl: number; iconEmoji?: string | null }[]>([]);
  recipeResults = signal<RecipeListItem[]>([]);
  reminders = signal<DietReminder[]>([]);
  suggestions = signal<DietSuggestion[]>([]);
  ingredientItems = signal<IngredientNutritionItem[]>([]);
  mealPreview = signal<MealPreview | null>(null);
  recentMeals = signal<RecentMeal[]>([]);
  favoriteRecipes = signal<FavoriteRecipe[]>([]);
  favoriteCatalog = signal<FavoriteCatalogItem[]>([]);
  history = signal<HistoryDay[]>([]);
  historyLoading = signal(false);
  historyRange = signal<HistoryRange>(7);
  editingReminderId = signal<number | null>(null);

  mealCategory = 2;
  recipeSearch = '';
  selectedRecipeId: number | null = null;
  customMealName = '';
  portions = 1;
  catalogItemId: number | null = null;
  grams = 150;
  goalsForm: UserDailyGoals = {};
  reminderType = DietReminderType.Water;
  reminderTitle = '';
  reminderTime = '09:00';

  readonly mealSort = signal<{ column: MealLogSortColumn; direction: SortDirection }>({
    column: 'name',
    direction: 'asc',
  });
  readonly drinkSort = signal<{ column: DrinkLogSortColumn; direction: SortDirection }>({
    column: 'name',
    direction: 'asc',
  });
  readonly reminderSort = signal<{ column: ReminderSortColumn; direction: SortDirection }>({
    column: 'time',
    direction: 'asc',
  });

  readonly sortedMeals = computed(() => {
    const meals = this.summary()?.meals ?? [];
    const { column, direction } = this.mealSort();
    return sortBy(meals, direction, (meal) => (column === 'name' ? meal.name : meal.calories));
  });

  readonly sortedDrinks = computed(() => {
    const drinks = this.summary()?.drinks ?? [];
    const { column, direction } = this.drinkSort();
    return sortBy(drinks, direction, (drink) => (column === 'name' ? drink.name : drink.volumeMl));
  });

  readonly sortedReminders = computed(() => {
    const { column, direction } = this.reminderSort();
    return sortBy(this.reminders(), direction, (reminder) =>
      column === 'title' ? reminder.title : reminder.timeOfDay,
    );
  });

  readonly historyMaxCalories = computed(() => {
    const days = this.history();
    if (days.length === 0) return 0;
    return Math.max(...days.map((d) => d.calories), 1);
  });

  ngOnInit(): void {
    this.loadSummary();
    this.loadQuickLogData();
    this.diet.getDrinkCatalog().subscribe({ next: (items) => this.catalog.set(items), error: () => this.catalog.set([]) });
    this.ingredients.getItems().subscribe({ next: (items) => this.ingredientItems.set(items), error: () => this.ingredientItems.set([]) });

    this.recipeSearch$.pipe(debounceTime(300), distinctUntilChanged(), switchMap((q) => (q.trim().length >= 2 ? this.recipes.getRecipes({ search: q }) : of([]))), takeUntil(this.destroy$)).subscribe((items) => this.recipeResults.set(items));

    this.preview$.pipe(debounceTime(250), takeUntil(this.destroy$)).subscribe(() => this.loadPreview());
  }

  ngOnDestroy(): void {
    this.destroy$.next();
    this.destroy$.complete();
  }

  readonly progressCards = computed(() => {
    const s = this.summary();
    if (!s) return [];
    const pct = (consumed: number, goal?: number | null) => (goal && goal > 0 ? Math.min((consumed / goal) * 100, 100) : 0);
    return [
      { labelKey: 'diet.calories', consumed: s.calories.consumed, goal: s.calories.goal, remaining: s.calories.remaining, unit: ' kcal', percent: pct(s.calories.consumed, s.calories.goal) },
      { labelKey: 'diet.water', consumed: s.waterMl.consumed, goal: s.waterMl.goal, remaining: s.waterMl.remaining, unit: 'ml', percent: pct(s.waterMl.consumed, s.waterMl.goal) },
      { labelKey: 'recipes.protein', consumed: Number(s.proteinG.consumed), goal: s.proteinG.goal != null ? Number(s.proteinG.goal) : null, remaining: s.proteinG.remaining != null ? Number(s.proteinG.remaining) : null, unit: 'g', percent: pct(Number(s.proteinG.consumed), s.proteinG.goal != null ? Number(s.proteinG.goal) : null) },
      { labelKey: 'recipes.fat', consumed: Number(s.fatG.consumed), goal: s.fatG.goal != null ? Number(s.fatG.goal) : null, remaining: s.fatG.remaining != null ? Number(s.fatG.remaining) : null, unit: 'g', percent: pct(Number(s.fatG.consumed), s.fatG.goal != null ? Number(s.fatG.goal) : null) },
      { labelKey: 'recipes.carbs', consumed: Number(s.carbsG.consumed), goal: s.carbsG.goal != null ? Number(s.carbsG.goal) : null, remaining: s.carbsG.remaining != null ? Number(s.carbsG.remaining) : null, unit: 'g', percent: pct(Number(s.carbsG.consumed), s.carbsG.goal != null ? Number(s.carbsG.goal) : null) },
    ];
  });

  formatReminderTime = formatReminderTime;

  mealsHeading(): string {
    return this.selectedDate() === todayIso()
      ? this.translate.instant('diet.mealsToday')
      : this.translate.instant('diet.mealsOnDate', { date: this.selectedDate() });
  }

  drinksHeading(): string {
    return this.selectedDate() === todayIso()
      ? this.translate.instant('diet.drinksToday')
      : this.translate.instant('diet.drinksOnDate', { date: this.selectedDate() });
  }

  shiftDate(days: number): void {
    this.selectedDate.update((d) => addDaysIso(d, days));
    this.loadSummary();
  }

  goToday(): void {
    this.selectedDate.set(todayIso());
    this.loadSummary();
  }

  setTab(id: Tab): void {
    this.tab.set(id);
    if (id === 'today') {
      this.loadSummary();
      this.loadQuickLogData();
    }
    if (id === 'goals') this.loadGoals();
    if (id === 'reminders') this.loadReminders();
    if (id === 'history') this.loadHistory();
  }

  setHistoryRange(range: HistoryRange): void {
    this.historyRange.set(range);
    this.loadHistory();
  }

  loadSummary(): void {
    const date = this.selectedDate();
    this.loading.set(true);
    this.error.set(null);
    this.diet.getSummary(date).subscribe({
      next: (data) => {
        this.summary.set(data);
        this.loading.set(false);
        this.loadSuggestions();
      },
      error: (err) => {
        this.error.set(extractApiError(err, this.translate.instant('errors.loadDietFailed')));
        this.summary.set(null);
        this.loading.set(false);
      },
    });
  }

  loadQuickLogData(): void {
    this.diet.getRecentMeals(8).subscribe({ next: (m) => this.recentMeals.set(m), error: () => this.recentMeals.set([]) });
    this.diet.getFavoriteRecipes().subscribe({ next: (f) => this.favoriteRecipes.set(f), error: () => this.favoriteRecipes.set([]) });
    this.diet.getFavoriteCatalogItems().subscribe({ next: (f) => this.favoriteCatalog.set(f), error: () => this.favoriteCatalog.set([]) });
  }

  loadHistory(): void {
    const to = todayIso();
    const from = addDaysIso(to, -(this.historyRange() - 1));
    this.historyLoading.set(true);
    this.diet.getHistory(from, to).subscribe({
      next: (days) => {
        this.history.set(days);
        this.historyLoading.set(false);
      },
      error: () => {
        this.history.set([]);
        this.historyLoading.set(false);
      },
    });
  }

  barHeight(calories: number): number {
    const max = this.historyMaxCalories();
    if (max <= 0) return 0;
    return Math.max((calories / max) * 100, 2);
  }

  dayLabel(iso: string): string {
    const d = new Date(`${iso}T12:00:00`);
    return d.toLocaleDateString(undefined, { day: 'numeric', month: 'short' });
  }

  loadSuggestions(): void {
    this.diet.getSuggestions(this.selectedDate()).subscribe({
      next: (items) => this.suggestions.set(items),
      error: () => this.suggestions.set([]),
    });
  }

  loadGoals(): void {
    this.diet.getGoals().subscribe((g) => (this.goalsForm = { ...g }));
  }

  loadReminders(): void {
    this.diet.getReminders().subscribe((items) => this.reminders.set(items));
  }

  onRecipeSearch(value: string): void {
    this.recipeSearch = value;
    this.selectedRecipeId = null;
    this.catalogItemId = null;
    this.mealPreview.set(null);
    this.recipeSearch$.next(value);
  }

  selectRecipe(id: number): void {
    this.selectedRecipeId = id;
    this.customMealName = '';
    this.catalogItemId = null;
    this.refreshPreview();
  }

  onCustomMealChange(value: string): void {
    this.customMealName = value;
    if (value.trim()) {
      this.selectedRecipeId = null;
      this.catalogItemId = null;
    }
    this.refreshPreview();
  }

  onCatalogItemChange(id: number | null): void {
    this.catalogItemId = id;
    if (id) {
      this.selectedRecipeId = null;
      this.customMealName = '';
    }
    this.refreshPreview();
  }

  refreshPreview(): void {
    this.preview$.next();
  }

  buildMealPayload(): LogMealInput | null {
    const date = this.selectedDate();
    if (this.selectedRecipeId) {
      return { recipeId: this.selectedRecipeId, mealCategory: this.mealCategory, logDate: date, portions: this.portions };
    }
    if (this.catalogItemId && this.grams > 0) {
      return { catalogItemId: this.catalogItemId, grams: this.grams, mealCategory: this.mealCategory, logDate: date };
    }
    if (this.customMealName.trim()) {
      return { name: this.customMealName.trim(), mealCategory: this.mealCategory, logDate: date };
    }
    return null;
  }

  loadPreview(): void {
    const payload = this.buildMealPayload();
    if (!payload) {
      this.mealPreview.set(null);
      return;
    }
    this.diet.previewMeal(payload, this.selectedDate()).subscribe({
      next: (preview) => this.mealPreview.set(preview),
      error: () => this.mealPreview.set(null),
    });
  }

  canLogMeal(): boolean {
    return this.buildMealPayload() != null;
  }

  logMeal(): void {
    const payload = this.buildMealPayload();
    if (!payload) return;
    this.busy.set(true);
    this.diet.logMeal(payload).subscribe({
      next: () => {
        this.resetMealForm();
        this.busy.set(false);
        this.loadSummary();
        this.loadQuickLogData();
      },
      error: () => this.busy.set(false),
    });
  }

  logRecent(m: RecentMeal): void {
    this.busy.set(true);
    const payload: LogMealInput = {
      mealCategory: m.mealCategory,
      logDate: this.selectedDate(),
      portions: m.portions,
    };
    if (m.recipeId) payload.recipeId = m.recipeId;
    else if (m.catalogItemId && m.grams) {
      payload.catalogItemId = m.catalogItemId;
      payload.grams = m.grams;
    } else payload.name = m.name;
    this.diet.logMeal(payload).subscribe({
      next: () => { this.busy.set(false); this.loadSummary(); },
      error: () => this.busy.set(false),
    });
  }

  logFavoriteRecipe(f: FavoriteRecipe): void {
    this.busy.set(true);
    this.diet.logMeal({ recipeId: f.recipeId, mealCategory: f.mealCategory, logDate: this.selectedDate(), portions: 1 }).subscribe({
      next: () => { this.busy.set(false); this.loadSummary(); },
      error: () => this.busy.set(false),
    });
  }

  logFavoriteCatalog(f: FavoriteCatalogItem): void {
    this.busy.set(true);
    this.diet.logMeal({ catalogItemId: f.catalogItemId, grams: 150, mealCategory: this.mealCategory, logDate: this.selectedDate() }).subscribe({
      next: () => { this.busy.set(false); this.loadSummary(); },
      error: () => this.busy.set(false),
    });
  }

  copyYesterday(): void {
    this.busy.set(true);
    this.diet.copyYesterdayMeals(this.selectedDate()).subscribe({
      next: () => { this.busy.set(false); this.loadSummary(); },
      error: () => this.busy.set(false),
    });
  }

  logSuggestion(s: DietSuggestion): void {
    this.busy.set(true);
    this.diet.logMeal({ catalogItemId: s.catalogItemId, grams: s.suggestedGrams, mealCategory: this.mealCategory, logDate: this.selectedDate() }).subscribe({
      next: () => {
        this.busy.set(false);
        this.loadSummary();
      },
      error: () => this.busy.set(false),
    });
  }

  resetMealForm(): void {
    this.recipeSearch = '';
    this.customMealName = '';
    this.selectedRecipeId = null;
    this.catalogItemId = null;
    this.portions = 1;
    this.grams = 150;
    this.recipeResults.set([]);
    this.mealPreview.set(null);
  }

  logDrinkFromCatalog(drinkCatalogId: number, volumeMl: number): void {
    this.busy.set(true);
    this.diet.logDrink({ drinkCatalogId, volumeMl, logDate: this.selectedDate() }).subscribe({
      next: () => { this.busy.set(false); this.loadSummary(); },
      error: () => this.busy.set(false),
    });
  }

  removeMeal(id: number): void {
    this.diet.deleteMeal(id).subscribe(() => this.loadSummary());
  }

  removeDrink(id: number): void {
    this.diet.deleteDrink(id).subscribe(() => this.loadSummary());
  }

  saveGoals(): void {
    this.busy.set(true);
    this.diet.saveGoals(this.goalsForm).subscribe({
      next: () => { this.busy.set(false); this.loadSummary(); },
      error: () => this.busy.set(false),
    });
  }

  startEditReminder(r: DietReminder): void {
    this.editingReminderId.set(r.id);
    this.reminderType = r.reminderType;
    this.reminderTitle = r.title;
    this.reminderTime = r.timeOfDay.slice(0, 5);
  }

  cancelEditReminder(): void {
    this.editingReminderId.set(null);
    this.reminderTitle = '';
    this.reminderTime = '09:00';
    this.reminderType = DietReminderType.Water;
  }

  saveReminder(): void {
    this.busy.set(true);
    const input = {
      reminderType: this.reminderType,
      title: this.reminderTitle.trim(),
      timeOfDay: this.reminderTime,
      daysOfWeekMask: REMINDER_DAYS_MASK_ALL,
    };
    const editId = this.editingReminderId();
    const req = editId
      ? this.diet.updateReminder(editId, input)
      : this.diet.createReminder(input);
    req.subscribe({
      next: () => {
        this.cancelEditReminder();
        this.busy.set(false);
        this.loadReminders();
      },
      error: () => this.busy.set(false),
    });
  }

  removeReminder(id: number): void {
    this.diet.deleteReminder(id).subscribe(() => this.loadReminders());
  }

  mealMacroLine(m: { calories?: number | null; proteinG?: number | null; fatG?: number | null; carbsG?: number | null }): string {
    if (m.calories == null) return this.translate.instant('diet.noMacros');
    return `${m.calories} kcal · P ${Number(m.proteinG ?? 0).toFixed(1)}g · F ${Number(m.fatG ?? 0).toFixed(1)}g · C ${Number(m.carbsG ?? 0).toFixed(1)}g`;
  }

  suggestionName(s: DietSuggestion): string {
    if (this.language.currentLanguage() === 'pl' && s.namePl) return s.namePl;
    return s.name;
  }

  displayIngredientName(item: IngredientNutritionItem): string {
    if (this.language.currentLanguage() === 'pl' && item.namePl) return item.namePl;
    return item.name;
  }

  favoriteCatalogName(f: FavoriteCatalogItem): string {
    if (this.language.currentLanguage() === 'pl' && f.namePl) return f.namePl;
    return f.name;
  }

  toggleMealSort(column: MealLogSortColumn): void {
    this.mealSort.update((current) => nextSort(current, column));
  }

  mealSortIndicator(column: MealLogSortColumn): string {
    return sortIndicator(this.mealSort(), column);
  }

  toggleDrinkSort(column: DrinkLogSortColumn): void {
    this.drinkSort.update((current) => nextSort(current, column));
  }

  drinkSortIndicator(column: DrinkLogSortColumn): string {
    return sortIndicator(this.drinkSort(), column);
  }

  toggleReminderSort(column: ReminderSortColumn): void {
    this.reminderSort.update((current) => nextSort(current, column));
  }

  reminderSortIndicator(column: ReminderSortColumn): string {
    return sortIndicator(this.reminderSort(), column);
  }
}
