import { CurrencyPipe, DatePipe, registerLocaleData } from '@angular/common';
import localeDe from '@angular/common/locales/de';
import { Component, LOCALE_ID, computed, effect, inject, signal } from '@angular/core';
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatCheckboxModule } from '@angular/material/checkbox';
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 { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
import { AuthStore } from '../../../core/auth/auth-store';
import { TeamStore } from '../../../core/team/team-store';
import { TransactionsApi } from '../../../core/team/transactions-api';
import {
  CreatePlayerTransaction,
  CreateTeamWalletTransaction,
  TeamActivity,
} from '../../../models/transaction.model';
import { ConfirmDialog, ConfirmDialogData } from '../../../shared/confirm-dialog/confirm-dialog';
import { splitAmounts } from './transaction-calculation';
import { signedTransactionAmount } from '../../../models/transaction-amount';

registerLocaleData(localeDe);

const HIGH_AMOUNT_CONFIRM_THRESHOLD = 300;

@Component({
  selector: 'app-cashbox',
  imports: [
    CurrencyPipe,
    DatePipe,
    ReactiveFormsModule,
    MatButtonModule,
    MatCardModule,
    MatCheckboxModule,
    MatFormFieldModule,
    MatIconModule,
    MatInputModule,
    MatProgressSpinnerModule,
    MatSelectModule,
    MatSnackBarModule,
  ],
  providers: [{ provide: LOCALE_ID, useValue: 'de-DE' }],
  templateUrl: './cashbox.html',
  styleUrl: './cashbox.scss',
})
export class Cashbox {
  private readonly authStore = inject(AuthStore);
  private readonly dialog = inject(MatDialog);
  private readonly formBuilder = inject(FormBuilder);
  private readonly snackBar = inject(MatSnackBar);
  private readonly teamStore = inject(TeamStore);
  private readonly transactionsApi = inject(TransactionsApi);
  private loadedTeamId: number | null = null;

  protected readonly team = this.teamStore.team;
  protected readonly activities = signal<TeamActivity[]>([]);
  protected readonly loading = signal(false);
  protected readonly saving = signal(false);
  protected readonly playerTransactionTypes = [
    { id: 0, label: 'Zahlung' },
    { id: 1, label: 'Guthaben' },
    { id: 11, label: 'Strafe' },
    { id: 12, label: 'Umlage' },
    { id: 13, label: 'Gebühr' },
  ];
  protected readonly teamTransactionTypes = [
    { id: 1, label: 'Guthaben' },
    { id: 14, label: 'Ausgabe' },
  ];

  protected readonly canBook = computed(() => {
    const user = this.authStore.currentUser();
    if (user?.role?.id === 1) return true;
    return (
      this.team()?.players?.some(
        (player) => player.user?.id === user?.id && (player.teamRole?.id ?? 0) >= 2,
      ) ?? false
    );
  });

  protected readonly activePlayers = computed(() =>
    (this.team()?.players ?? []).filter((player) => player.active),
  );

  protected readonly playerForm = this.formBuilder.nonNullable.group({
    playerIds: this.formBuilder.nonNullable.control<number[]>([], Validators.required),
    amount: [0, [Validators.required, Validators.min(0.01), Validators.max(10000)]],
    type: [11, Validators.required],
    note: [''],
    date: [this.today(), Validators.required],
    total: [false],
  });

  protected readonly teamForm = this.formBuilder.nonNullable.group({
    amount: [0, [Validators.required, Validators.min(0.01), Validators.max(10000)]],
    type: [14, Validators.required],
    note: [''],
    date: [this.today(), Validators.required],
  });

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

  protected submitPlayerBooking(): void {
    if (!this.canBook() || this.playerForm.invalid || this.saving()) return;
    const value = this.playerForm.getRawValue();
    if (value.playerIds.length === 0) return;

    const amounts = splitAmounts(value.amount, value.playerIds.length, value.total);
    const transactions: CreatePlayerTransaction[] = value.playerIds.map((playerId, index) => ({
      playerId,
      amount: amounts[index],
      type: value.type,
      note: value.note.trim() || null,
      date: this.toIsoDate(value.date),
    }));

    if (value.amount >= HIGH_AMOUNT_CONFIRM_THRESHOLD) {
      this.confirm({
        title: 'Betrag prüfen',
        message: `Die Buchung über ${value.amount.toFixed(2)} € ist ungewöhnlich hoch. Wirklich fortfahren?`,
        confirmLabel: 'Buchen',
      }).subscribe((confirmed) => {
        if (confirmed) this.createPlayerTransactions(transactions);
      });
      return;
    }

    this.createPlayerTransactions(transactions);
  }

  protected submitTeamBooking(): void {
    const team = this.team();
    if (!this.canBook() || !team || this.teamForm.invalid || this.saving()) return;
    const value = this.teamForm.getRawValue();
    const transaction: CreateTeamWalletTransaction = {
      teamId: team.id,
      amount: value.amount,
      type: value.type,
      note: value.note.trim() || null,
      date: this.toIsoDate(value.date),
    };

    const save = () => this.createTeamWalletTransaction(transaction);
    if (value.amount >= HIGH_AMOUNT_CONFIRM_THRESHOLD) {
      this.confirm({
        title: 'Betrag prüfen',
        message: `Die Buchung über ${value.amount.toFixed(2)} € ist ungewöhnlich hoch. Wirklich fortfahren?`,
        confirmLabel: 'Buchen',
      }).subscribe((confirmed) => {
        if (confirmed) save();
      });
      return;
    }
    save();
  }

  protected canReverse(activity: TeamActivity): boolean {
    return (
      this.canBook() &&
      !activity.isTeamWalletTransaction &&
      !activity.note?.startsWith('Stornierung von Buchung #')
    );
  }

  protected reverseBooking(activity: TeamActivity): void {
    if (!this.canReverse(activity) || this.saving()) return;
    this.confirm({
      title: 'Buchung stornieren',
      message: `Die Buchung über ${activity.amount.toFixed(2)} € wirklich stornieren? Die Originalbuchung bleibt sichtbar.`,
      confirmLabel: 'Stornieren',
    }).subscribe((confirmed) => {
      if (!confirmed) return;
      this.saving.set(true);
      this.transactionsApi.reverseTransaction(activity.id).subscribe({
        next: () => this.afterMutation('Buchung wurde storniert.'),
        error: () => this.handleError('Buchung konnte nicht storniert werden.'),
      });
    });
  }

  protected typeLabel(type: string): string {
    return (
      {
        payment: 'Zahlung',
        credit: 'Guthaben',
        fine: 'Strafe',
        levy: 'Umlage',
        fee: 'Gebühr',
        expense: 'Ausgabe',
      }[type] ?? type
    );
  }

  protected displayAmount(activity: TeamActivity): number {
    return signedTransactionAmount(activity.amount, activity.type);
  }

  private createPlayerTransactions(transactions: CreatePlayerTransaction[]): void {
    this.saving.set(true);
    this.transactionsApi.createPlayerTransactions(transactions).subscribe({
      next: () => {
        this.playerForm.reset({
          playerIds: [],
          amount: 0,
          type: 11,
          note: '',
          date: this.today(),
          total: false,
        });
        this.afterMutation('Buchung wurde gespeichert.');
      },
      error: () => this.handleError('Buchung konnte nicht gespeichert werden.'),
    });
  }

  private createTeamWalletTransaction(transaction: CreateTeamWalletTransaction): void {
    this.saving.set(true);
    this.transactionsApi.createTeamWalletTransaction(transaction).subscribe({
      next: () => {
        this.teamForm.reset({ amount: 0, type: 14, note: '', date: this.today() });
        this.afterMutation('Teambuchung wurde gespeichert.');
      },
      error: () => this.handleError('Teambuchung konnte nicht gespeichert werden.'),
    });
  }

  private afterMutation(message: string): void {
    this.saving.set(false);
    this.snackBar.open(message, undefined, { duration: 4000 });
    this.teamStore.refreshTeam();
    const teamId = this.team()?.id;
    if (teamId) this.loadActivities(teamId);
  }

  private loadActivities(teamId: number): void {
    this.loading.set(true);
    this.transactionsApi.loadTeamTransactions(teamId).subscribe({
      next: (activities) => {
        this.activities.set(activities);
        this.loading.set(false);
      },
      error: () => {
        this.activities.set([]);
        this.loading.set(false);
        this.snackBar.open('Buchungen konnten nicht geladen werden.', undefined, {
          duration: 5000,
        });
      },
    });
  }

  private handleError(message: string): void {
    this.saving.set(false);
    this.snackBar.open(message, undefined, { duration: 5000 });
  }

  private confirm(data: ConfirmDialogData) {
    return this.dialog.open(ConfirmDialog, { data }).afterClosed();
  }

  private today(): string {
    const now = new Date();
    const offset = now.getTimezoneOffset() * 60_000;
    return new Date(now.getTime() - offset).toISOString().slice(0, 10);
  }

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