import { CurrencyPipe, DatePipe, registerLocaleData } from '@angular/common';
import localeDe from '@angular/common/locales/de';
import { HttpErrorResponse } from '@angular/common/http';
import { Component, DestroyRef, LOCALE_ID, computed, effect, inject, signal } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatDialog } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSelectModule } from '@angular/material/select';
import { RouterLink } from '@angular/router';
import { EMPTY, Observable, catchError, finalize, switchMap, take, tap } from 'rxjs';
import { RecurringTransactionApi } from '../../../../core/team/recurring-transaction-api';
import { TeamPermissionsService } from '../../../../core/team/team-permissions';
import { TeamStore } from '../../../../core/team/team-store';
import {
  RecurringTransaction,
  RecurringTransactionInterval,
} from '../../../../models/recurring-transaction.model';
import { ConfirmDialog } from '../../../../shared/confirm-dialog/confirm-dialog';

registerLocaleData(localeDe);

const TYPE_OPTIONS = [
  { id: 13, label: 'Gebühr' },
  { id: 12, label: 'Umlage' },
];

const INTERVAL_OPTIONS: { value: RecurringTransactionInterval; label: string }[] = [
  { value: 'monthly', label: 'Monatlich' },
  { value: 'quarterly', label: 'Quartalsweise' },
  { value: 'yearly', label: 'Jährlich' },
];

@Component({
  selector: 'app-recurring-transactions',
  imports: [
    CurrencyPipe,
    DatePipe,
    ReactiveFormsModule,
    RouterLink,
    MatButtonModule,
    MatCardModule,
    MatFormFieldModule,
    MatIconModule,
    MatInputModule,
    MatProgressSpinnerModule,
    MatSelectModule,
  ],
  providers: [{ provide: LOCALE_ID, useValue: 'de-DE' }],
  templateUrl: './recurring-transactions.html',
  styleUrl: './recurring-transactions.scss',
})
export class RecurringTransactions {
  private readonly destroyRef = inject(DestroyRef);
  private readonly dialog = inject(MatDialog);
  private readonly formBuilder = inject(FormBuilder);
  private readonly api = inject(RecurringTransactionApi);
  private readonly permissions = inject(TeamPermissionsService);
  private readonly teamStore = inject(TeamStore);
  private loadedTeamId: number | null = null;

  protected readonly typeOptions = TYPE_OPTIONS;
  protected readonly intervalOptions = INTERVAL_OPTIONS;
  protected readonly team = this.teamStore.team;
  protected readonly entries = signal<RecurringTransaction[]>([]);
  protected readonly loading = signal(false);
  protected readonly saving = signal(false);
  protected readonly pendingEntryId = signal<number | null>(null);
  protected readonly editingEntryId = signal<number | null>(null);
  protected readonly loadError = signal<string | null>(null);
  protected readonly mutationError = signal<string | null>(null);
  protected readonly mutationPending = computed(
    () => this.saving() || this.pendingEntryId() !== null,
  );
  protected readonly canManage = computed(() =>
    this.permissions.canDo(this.team(), 'transactionCreate'),
  );
  protected readonly form = this.formBuilder.nonNullable.group({
    description: ['', [Validators.required, Validators.maxLength(120), Validators.pattern(/\S/)]],
    amount: [
      0,
      [
        Validators.required,
        Validators.min(0.01),
        Validators.max(10000),
        Validators.pattern(/^\d+(\.\d{1,2})?$/),
      ],
    ],
    type: [13, Validators.required],
    interval: ['monthly' as RecurringTransactionInterval, Validators.required],
    startDate: ['', Validators.required],
  });
  protected readonly editForm = this.formBuilder.nonNullable.group({
    description: ['', [Validators.required, Validators.maxLength(120), Validators.pattern(/\S/)]],
    amount: [
      0,
      [
        Validators.required,
        Validators.min(0.01),
        Validators.max(10000),
        Validators.pattern(/^\d+(\.\d{1,2})?$/),
      ],
    ],
    type: [13, Validators.required],
    interval: ['monthly' as RecurringTransactionInterval, Validators.required],
  });

  constructor() {
    effect(() => {
      const teamId = this.team()?.id;
      if (teamId && teamId !== this.loadedTeamId) {
        this.loadedTeamId = teamId;
        this.load(teamId);
      }
    });
  }

  protected createEntry(): void {
    const team = this.team();
    if (!this.canManage() || !team || this.form.invalid || this.mutationPending()) return;
    this.saving.set(true);
    this.mutationError.set(null);
    const { description, amount, type, interval, startDate } = this.form.getRawValue();
    this.api
      .createRecurringTransaction({
        teamId: team.id,
        description,
        amount,
        type,
        interval,
        startDate: this.toIso(startDate),
      })
      .pipe(
        tap(() =>
          this.form.reset({ description: '', amount: 0, type: 13, interval: 'monthly', startDate: '' }),
        ),
        switchMap(() => this.reloadAfterMutation(team.id)),
        finalize(() => this.saving.set(false)),
        takeUntilDestroyed(this.destroyRef),
      )
      .subscribe({
        next: (entries) => this.entries.set(entries),
        error: (error: HttpErrorResponse) =>
          this.mutationError.set(this.errorMessage(error, 'Eintrag konnte nicht angelegt werden.')),
      });
  }

  protected startEdit(entry: RecurringTransaction): void {
    if (!this.canManage() || this.mutationPending()) return;
    this.editingEntryId.set(entry.id);
    this.editForm.setValue({
      description: entry.description,
      amount: entry.amount,
      type: entry.type,
      interval: entry.interval,
    });
    this.mutationError.set(null);
  }

  protected cancelEdit(): void {
    if (this.pendingEntryId() !== null) return;
    this.editingEntryId.set(null);
    this.mutationError.set(null);
  }

  protected saveEntry(entry: RecurringTransaction): void {
    const teamId = this.team()?.id;
    if (
      !this.canManage() ||
      !teamId ||
      this.editingEntryId() !== entry.id ||
      this.editForm.invalid ||
      this.mutationPending()
    ) {
      return;
    }
    this.pendingEntryId.set(entry.id);
    this.mutationError.set(null);
    this.api
      .updateRecurringTransaction(entry.id, { ...this.editForm.getRawValue(), active: entry.active })
      .pipe(
        tap(() => this.editingEntryId.set(null)),
        switchMap(() => this.reloadAfterMutation(teamId)),
        finalize(() => this.pendingEntryId.set(null)),
        takeUntilDestroyed(this.destroyRef),
      )
      .subscribe({
        next: (entries) => this.entries.set(entries),
        error: (error: HttpErrorResponse) =>
          this.mutationError.set(this.errorMessage(error, 'Eintrag konnte nicht gespeichert werden.')),
      });
  }

  protected toggleActive(entry: RecurringTransaction): void {
    const teamId = this.team()?.id;
    if (!this.canManage() || !teamId || this.mutationPending()) return;
    this.pendingEntryId.set(entry.id);
    this.mutationError.set(null);
    this.api
      .updateRecurringTransaction(entry.id, {
        description: entry.description,
        amount: entry.amount,
        type: entry.type,
        interval: entry.interval,
        active: !entry.active,
      })
      .pipe(
        switchMap(() => this.reloadAfterMutation(teamId)),
        finalize(() => this.pendingEntryId.set(null)),
        takeUntilDestroyed(this.destroyRef),
      )
      .subscribe({
        next: (entries) => this.entries.set(entries),
        error: (error: HttpErrorResponse) =>
          this.mutationError.set(this.errorMessage(error, 'Eintrag konnte nicht geändert werden.')),
      });
  }

  protected confirmDelete(entry: RecurringTransaction): void {
    if (!this.canManage() || this.mutationPending()) return;
    this.dialog
      .open(ConfirmDialog, {
        data: {
          title: 'Eintrag löschen?',
          message: `„${entry.description}“ wird endgültig gelöscht.`,
          confirmLabel: 'Löschen',
        },
        restoreFocus: true,
      })
      .afterClosed()
      .pipe(take(1), takeUntilDestroyed(this.destroyRef))
      .subscribe((confirmed) => {
        if (confirmed) this.deleteEntry(entry);
      });
  }

  protected retryLoad(): void {
    const teamId = this.team()?.id;
    if (teamId) this.load(teamId);
  }

  private deleteEntry(entry: RecurringTransaction): void {
    const teamId = this.team()?.id;
    if (!teamId) return;
    this.pendingEntryId.set(entry.id);
    this.mutationError.set(null);
    this.api
      .deleteRecurringTransaction(entry.id)
      .pipe(
        switchMap(() => this.reloadAfterMutation(teamId)),
        finalize(() => this.pendingEntryId.set(null)),
        takeUntilDestroyed(this.destroyRef),
      )
      .subscribe({
        next: (entries) => this.entries.set(entries),
        error: (error: HttpErrorResponse) =>
          this.mutationError.set(this.errorMessage(error, 'Eintrag konnte nicht gelöscht werden.')),
      });
  }

  private reloadAfterMutation(teamId: number): Observable<RecurringTransaction[]> {
    return this.api.loadRecurringTransactions(teamId).pipe(
      catchError(() => {
        this.loadError.set(
          'Änderung wurde gespeichert, aber die Liste konnte nicht aktualisiert werden.',
        );
        return EMPTY;
      }),
    );
  }

  private load(teamId: number): void {
    this.loading.set(true);
    this.loadError.set(null);
    this.api
      .loadRecurringTransactions(teamId)
      .pipe(
        finalize(() => this.loading.set(false)),
        takeUntilDestroyed(this.destroyRef),
      )
      .subscribe({
        next: (entries) => this.entries.set(entries),
        error: () => {
          this.entries.set([]);
          this.loadError.set('Liste konnte nicht geladen werden.');
        },
      });
  }

  private toIso(date: string): string {
    return new Date(`${date}T12:00:00`).toISOString();
  }

  private errorMessage(error: HttpErrorResponse, fallback: string): string {
    const detail = typeof error.error?.message === 'string' ? error.error.message : '';
    if (error.status === 403) return 'Keine Berechtigung für diese Änderung.';
    if (error.status === 404) return 'Der Eintrag wurde nicht gefunden. Bitte lade die Liste neu.';
    if (error.status === 422) return 'Bitte prüfe die Eingaben.';
    return detail || fallback;
  }
}
