import { Component, computed, inject, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { RouterLink } from '@angular/router';
import { TranslatePipe, TranslateService } from '@ngx-translate/core';
import { switchMap } from 'rxjs';
import { RecipeGeneratorService } from '../../core/services/recipe-generator.service';
import {
  CommitRecipeResult,
  GeneratedRecipeDraft,
  RecipeGeneratorConstraints,
} from '../../core/models/generator.models';
import { MEAL_CATEGORY_I18N_KEYS, MealCategory } from '../../core/models/recipe.models';
import { extractApiError } from '../../core/utils/api-error.util';

type Step = 'constraints' | 'review' | 'done';

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

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

      @if (error()) {
        <div role="alert" class="rounded-xl bg-red-50 p-3 text-sm text-red-700 dark:bg-red-950/40 dark:text-red-300">
          {{ error() }}
        </div>
      }

      @if (step() === 'constraints') {
        <section class="card space-y-4 p-6">
          <h2 class="text-lg font-bold">{{ 'generators.recipe.constraintsTitle' | translate }}</h2>
          <label class="block text-sm">
            {{ 'generators.recipe.prompt' | translate }}
            <textarea
              class="input mt-1 w-full"
              rows="3"
              [(ngModel)]="constraints.prompt"
              [placeholder]="'generators.recipe.promptPlaceholder' | translate"
            ></textarea>
          </label>
          <label class="block text-sm">
            {{ 'generators.recipe.targetCalories' | translate }}
            <input
              type="number"
              min="50"
              max="3000"
              class="input mt-1 w-full"
              [(ngModel)]="constraints.targetCalories"
            />
            <span class="mt-1 block text-xs text-slate-500 dark:text-slate-400">
              {{ 'generators.recipe.targetCaloriesHint' | translate }}
            </span>
          </label>
          <label class="block text-sm">
            {{ 'generators.recipe.recipeCount' | translate }}
            <input type="number" min="1" max="8" class="input mt-1 w-full" [(ngModel)]="recipeCount" />
          </label>
          <label class="block text-sm">
            {{ 'common.category' | translate }}
            <select class="select mt-1 w-full" [(ngModel)]="constraints.mealCategory">
              @for (cat of mealCategories; track cat) {
                <option [ngValue]="cat">{{ categoryLabel(cat) | translate }}</option>
              }
            </select>
          </label>
          <label class="block text-sm">
            {{ 'generators.recipe.maxPrep' | translate }}
            <input type="number" class="input mt-1 w-full" [(ngModel)]="constraints.maxPrepTimeMinutes" />
          </label>
          <label class="block text-sm">
            {{ 'generators.recipe.exclusions' | translate }}
            <input class="input mt-1 w-full" [(ngModel)]="constraints.exclusions" />
          </label>
          <button
            type="button"
            class="btn-primary"
            [disabled]="pending() || !constraints.prompt.trim()"
            (click)="runGenerate()"
          >
            {{ 'generators.recipe.generate' | translate }}
          </button>
        </section>
      }

      @if (step() === 'review' && drafts().length > 0) {
        <section class="space-y-4">
          <div class="flex flex-wrap items-center justify-between gap-2">
            <p class="text-sm text-slate-500 dark:text-slate-400">
              {{ 'generators.recipe.reviewHint' | translate: { count: drafts().length } }}
            </p>
            <label class="flex items-center gap-2 text-sm">
              <input type="checkbox" [checked]="allSelected()" (change)="toggleAll()" />
              {{ 'generators.recipe.selectAll' | translate }}
            </label>
          </div>

          @for (draft of drafts(); track draft.draftId) {
            <div class="card space-y-3 p-6">
              <div class="flex flex-wrap items-start justify-between gap-3">
                <label class="flex items-start gap-2">
                  <input
                    type="checkbox"
                    class="mt-1"
                    [checked]="selectedIds().has(draft.draftId)"
                    (change)="toggleSelected(draft.draftId)"
                  />
                  <div>
                    <h2 class="text-xl font-bold">{{ draft.name }}</h2>
                    @if (draft.calories != null) {
                      <p
                        class="text-sm"
                        [class.text-amber-700]="constraints.targetCalories && !isWithinCalorieTarget(draft.calories)"
                        [class.text-slate-500]="!constraints.targetCalories || isWithinCalorieTarget(draft.calories)"
                        [class.dark:text-slate-400]="!constraints.targetCalories || isWithinCalorieTarget(draft.calories)"
                      >
                        @if (constraints.targetCalories) {
                          {{
                            'generators.recipe.caloriesWithTarget'
                              | translate: { actual: draft.calories, target: constraints.targetCalories }
                          }}
                        } @else {
                          {{ draft.calories }} kcal
                        }
                        · P {{ draft.protein ?? 0 }}g · F {{ draft.fat ?? 0 }}g · C {{ draft.carbs ?? 0 }}g
                      </p>
                    }
                  </div>
                </label>
                <button type="button" class="btn-secondary !px-2 !py-1 text-xs" [disabled]="pending()" (click)="removeDraft(draft.draftId)">
                  {{ 'generators.recipe.remove' | translate }}
                </button>
              </div>

              @if (draft.unlinkedIngredientCount > 0) {
                <p class="text-sm text-amber-700">
                  {{ 'manage.unlinkedIngredientsWarning' | translate: { count: draft.unlinkedIngredientCount } }}
                </p>
              }

              <h3 class="font-semibold">{{ 'recipes.ingredients' | translate }}</h3>
              <ul class="space-y-1 text-sm">
                @for (ing of draft.ingredients; track $index) {
                  <li [class.text-amber-700]="!ing.isLinked && ing.amountGrams">
                    {{ ing.name }} — {{ ing.amountGrams ?? '?' }}g {{ ing.unit ?? '' }}
                  </li>
                }
              </ul>
              <h3 class="font-semibold">{{ 'manage.preparationSteps' | translate }}</h3>
              <ol class="list-decimal space-y-1 pl-5 text-sm">
                @for (s of draft.steps; track s.stepNumber) {
                  <li>{{ s.description }}</li>
                }
              </ol>

              <div class="flex flex-wrap gap-2">
                <button type="button" class="btn-secondary !px-2 !py-1 text-xs" [disabled]="pending()" (click)="regen(draft.draftId, 'ingredients')">
                  {{ 'generators.recipe.regenIngredients' | translate }}
                </button>
                <button type="button" class="btn-secondary !px-2 !py-1 text-xs" [disabled]="pending()" (click)="regen(draft.draftId, 'steps')">
                  {{ 'generators.recipe.regenSteps' | translate }}
                </button>
                <button type="button" class="btn-secondary !px-2 !py-1 text-xs" [disabled]="pending()" (click)="regen(draft.draftId, 'full')">
                  {{ 'generators.recipe.regenFull' | translate }}
                </button>
              </div>
            </div>
          }

          <div class="flex flex-wrap gap-2">
            <button type="button" class="btn-secondary" (click)="step.set('constraints')">
              {{ 'common.back' | translate }}
            </button>
            <button type="button" class="btn-primary" [disabled]="pending() || selectedIds().size === 0" (click)="saveSelected()">
              {{ 'generators.recipe.saveSelected' | translate: { count: selectedIds().size } }}
            </button>
          </div>
        </section>
      }

      @if (step() === 'done' && savedRecipes().length > 0) {
        <div class="card space-y-3 p-6">
          <p class="font-semibold">{{ 'generators.recipe.doneMultiple' | translate: { count: savedRecipes().length } }}</p>
          <ul class="space-y-2">
            @for (r of savedRecipes(); track r.recipeId) {
              <li>
                <a [routerLink]="['/recipes', r.recipeId]" class="text-brand-600 hover:underline">{{ r.recipeName }}</a>
              </li>
            }
          </ul>
          <button type="button" class="btn-secondary" (click)="reset()">
            {{ 'generators.recipe.generateMore' | translate }}
          </button>
        </div>
      }
    </div>
  `,
})
export class RecipeGeneratorComponent {
  private readonly recipeGenerator = inject(RecipeGeneratorService);
  private readonly translate = inject(TranslateService);

  readonly mealCategories = MEAL_CATEGORIES;
  readonly step = signal<Step>('constraints');
  readonly sessionId = signal<number | null>(null);
  readonly drafts = signal<GeneratedRecipeDraft[]>([]);
  readonly selectedIds = signal<Set<string>>(new Set());
  readonly savedRecipes = signal<CommitRecipeResult[]>([]);
  readonly pending = signal(false);
  readonly error = signal<string | null>(null);

  recipeCount = 3;
  constraints: RecipeGeneratorConstraints = {
    prompt: '',
    mealCategory: MealCategory.Breakfast,
    targetCalories: null,
    calorieToleranceKcal: 10,
    maxPrepTimeMinutes: 30,
    cuisineStyle: '',
    dietStyle: '',
    exclusions: '',
  };

  isWithinCalorieTarget(calories?: number | null): boolean {
    if (!this.constraints.targetCalories || calories == null) return true;
    const tolerance = this.constraints.calorieToleranceKcal ?? 10;
    return Math.abs(calories - this.constraints.targetCalories) <= tolerance;
  }

  readonly allSelected = computed(() => {
    const list = this.drafts();
    const selected = this.selectedIds();
    return list.length > 0 && list.every((d) => selected.has(d.draftId));
  });

  categoryLabel(cat: MealCategory): string {
    return MEAL_CATEGORY_I18N_KEYS[cat];
  }

  private syncDrafts(next: GeneratedRecipeDraft[]): void {
    this.drafts.set(next);
    this.selectedIds.set(new Set(next.map((d) => d.draftId)));
  }

  runGenerate(): void {
    this.error.set(null);
    this.pending.set(true);
    const count = Math.min(8, Math.max(1, this.recipeCount || 3));
    const existingId = this.sessionId();
    const request$ = existingId
      ? this.recipeGenerator.generateDrafts(existingId, count)
      : this.recipeGenerator
          .createSession(this.constraints)
          .pipe(
            switchMap((created) => {
              this.sessionId.set(created.id);
              return this.recipeGenerator.generateDrafts(created.id, count);
            }),
          );

    request$.subscribe({
      next: (session) => {
        if (!this.sessionId()) this.sessionId.set(session.id);
        this.syncDrafts(session.drafts ?? []);
        this.step.set('review');
        this.pending.set(false);
      },
      error: (e) => {
        this.error.set(extractApiError(e, this.translate.instant('generators.recipe.error')));
        this.pending.set(false);
      },
    });
  }

  regen(draftId: string, mode: 'ingredients' | 'steps' | 'full'): void {
    const id = this.sessionId();
    if (!id) return;
    this.pending.set(true);
    this.error.set(null);
    this.recipeGenerator.regeneratePart(id, draftId, mode, this.constraints.prompt).subscribe({
      next: (session) => {
        this.syncDrafts(session.drafts ?? []);
        this.pending.set(false);
      },
      error: (e) => {
        this.error.set(extractApiError(e, this.translate.instant('generators.recipe.error')));
        this.pending.set(false);
      },
    });
  }

  removeDraft(draftId: string): void {
    const id = this.sessionId();
    if (!id) return;
    this.pending.set(true);
    this.error.set(null);
    this.recipeGenerator.removeDraft(id, draftId).subscribe({
      next: (session) => {
        const next = session.drafts ?? [];
        this.syncDrafts(next);
        if (next.length === 0) this.step.set('constraints');
        this.pending.set(false);
      },
      error: (e) => {
        this.error.set(extractApiError(e, this.translate.instant('generators.recipe.error')));
        this.pending.set(false);
      },
    });
  }

  saveSelected(): void {
    const id = this.sessionId();
    const ids = [...this.selectedIds()];
    if (!id || ids.length === 0) return;
    this.pending.set(true);
    this.error.set(null);
    this.recipeGenerator.commitRecipes(id, ids).subscribe({
      next: (result) => {
        this.savedRecipes.set(result.saved);
        const remaining = this.drafts().filter((d) => !this.selectedIds().has(d.draftId));
        if (remaining.length === 0) {
          this.step.set('done');
        } else {
          this.syncDrafts(remaining);
        }
        this.pending.set(false);
      },
      error: (e) => {
        this.error.set(extractApiError(e, this.translate.instant('generators.recipe.error')));
        this.pending.set(false);
      },
    });
  }

  toggleSelected(draftId: string): void {
    this.selectedIds.update((prev) => {
      const next = new Set(prev);
      if (next.has(draftId)) next.delete(draftId);
      else next.add(draftId);
      return next;
    });
  }

  toggleAll(): void {
    if (this.allSelected()) {
      this.selectedIds.set(new Set());
    } else {
      this.selectedIds.set(new Set(this.drafts().map((d) => d.draftId)));
    }
  }

  reset(): void {
    this.step.set('constraints');
    this.sessionId.set(null);
    this.drafts.set([]);
    this.selectedIds.set(new Set());
    this.savedRecipes.set([]);
    this.error.set(null);
  }
}
