import { HttpErrorResponse } from '@angular/common/http';
import { Component, DestroyRef, inject, signal } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { RouterLink } from '@angular/router';
import { MatButtonModule } from '@angular/material/button';
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 { EMPTY, Subject, catchError, filter, finalize, of, startWith, switchMap, take, tap } from 'rxjs';
import { AuthStore } from '../../core/auth/auth-store';
import { AdminUsersApi } from '../../core/users/admin-users-api';
import { UsersApi } from '../../core/users/users-api';
import {
  AdminUserDirectorySummary,
  AdminUserStatusId,
  UserDirectoryPage,
  UserDirectoryRecord,
} from '../../models/user-directory.model';
import { ConfirmDialog } from '../../shared/confirm-dialog/confirm-dialog';
import { PlayerAssignments } from './player-assignments';
import { UserEdit, UserEditValue } from './user-edit';

@Component({
  selector: 'app-users',
  imports: [
    RouterLink,
    MatButtonModule,
    MatFormFieldModule,
    MatIconModule,
    MatInputModule,
    MatProgressSpinnerModule,
    PlayerAssignments,
    UserEdit,
  ],
  templateUrl: './users.html',
  styleUrl: './users.scss',
})
export class Users {
  private readonly usersApi = inject(UsersApi);
  private readonly adminUsersApi = inject(AdminUsersApi);
  private readonly authStore = inject(AuthStore);
  private readonly dialog = inject(MatDialog);
  private readonly destroyRef = inject(DestroyRef);
  private readonly directoryRequests = new Subject<boolean>();

  protected readonly isAdmin = this.authStore.isGlobalAdmin;
  protected readonly currentUser = this.authStore.currentUser;
  protected readonly directory = signal<UserDirectoryPage | null>(null);
  protected readonly loading = signal(true);
  protected readonly loadError = signal<string | null>(null);
  protected readonly mutationError = signal<string | null>(null);
  protected readonly searchDraft = signal('');
  protected readonly search = signal('');
  protected readonly page = signal(1);
  protected readonly limit = 20;
  protected readonly editingUserId = signal<number | null>(null);
  protected readonly assignmentUserId = signal<number | null>(null);
  protected readonly assignmentBusyUserId = signal<number | null>(null);
  protected readonly pendingUserId = signal<number | null>(null);

  constructor() {
    this.directoryRequests
      .pipe(
        startWith(true),
        switchMap((showLoading) => {
          if (showLoading) this.loading.set(true);
          this.loadError.set(null);
          const search = this.search();
          return this.usersApi
            .loadDirectory({ page: this.page(), limit: this.limit, ...(search ? { search } : {}) })
            .pipe(
              tap((directory) => this.directory.set(directory)),
              catchError((error: HttpErrorResponse) => {
                this.directory.set(null);
                this.loadError.set(this.errorMessage(error, 'Benutzer konnten nicht geladen werden.'));
                return EMPTY;
              }),
              finalize(() => this.loading.set(false)),
            );
        }),
        takeUntilDestroyed(this.destroyRef),
      )
      .subscribe();
  }

  protected loadDirectory(showLoading = true): void {
    this.directoryRequests.next(showLoading);
  }

  protected submitSearch(): void {
    if (this.assignmentBusyUserId() !== null) return;
    const search = this.searchDraft().trim();
    if (this.loading() && search === this.search() && this.page() === 1) return;
    this.search.set(search);
    this.page.set(1);
    this.loadDirectory();
  }

  protected previousPage(): void {
    if (this.page() <= 1 || this.loading() || this.assignmentBusyUserId() !== null) return;
    this.page.update((value) => value - 1);
    this.loadDirectory();
  }

  protected nextPage(): void {
    if (!this.directory()?.hasNextPage || this.loading() || this.assignmentBusyUserId() !== null) return;
    this.page.update((value) => value + 1);
    this.loadDirectory();
  }

  protected adminDetails(user: UserDirectoryRecord): AdminUserDirectorySummary | null {
    if (!this.isAdmin()) return null;
    return 'email' in user && 'role' in user ? user : null;
  }

  protected fullName(user: UserDirectoryRecord): string {
    return [user.firstName, user.lastName].filter(Boolean).join(' ') || `Benutzer ${user.id}`;
  }

  protected initials(user: UserDirectoryRecord): string {
    const value = `${user.firstName?.charAt(0) ?? ''}${user.lastName?.charAt(0) ?? ''}`.trim();
    return value || '?';
  }

  protected statusName(statusId?: number): string {
    if (statusId === 1) return 'Aktiv';
    if (statusId === 2) return 'Inaktiv';
    return 'Unbekannt';
  }

  protected roleName(roleId?: number): string {
    if (roleId === 1) return 'Administrator';
    if (roleId === 2) return 'Benutzer';
    return 'Unbekannt';
  }

  protected teamRoleName(name?: string): string {
    return (
      {
        player: 'Spieler',
        scnd_treasurer: '2. Kassenwart',
        captain: 'Kapitän',
        treasurer: 'Kassenwart',
        coach: 'Trainer',
      }[name ?? ''] ?? 'Spieler'
    );
  }

  protected toggleEdit(userId: number): void {
    if (this.assignmentBusyUserId() !== null) return;
    this.assignmentUserId.set(null);
    this.editingUserId.update((value) => (value === userId ? null : userId));
    this.mutationError.set(null);
  }

  protected toggleAssignments(userId: number): void {
    if (this.assignmentBusyUserId() !== null) return;
    this.editingUserId.set(null);
    this.assignmentUserId.update((value) => (value === userId ? null : userId));
    this.mutationError.set(null);
  }

  protected saveEdit(user: AdminUserDirectorySummary, value: UserEditValue): void {
    if (this.pendingUserId() !== null) return;
    this.pendingUserId.set(user.id);
    this.mutationError.set(null);
    const profileRequest = this.adminUsersApi.updateProfile(user.id, {
      firstName: value.firstName,
      lastName: value.lastName,
    });
    const roleId = user.role?.id;
    let profileSaved = false;
    profileRequest
      .pipe(
        tap((updatedUser) => {
          profileSaved = true;
          const currentUser = this.currentUser();
          if (currentUser?.id === updatedUser.id) {
            this.authStore.updateUser({
              ...currentUser,
              firstName: updatedUser.firstName,
              lastName: updatedUser.lastName,
            });
          }
        }),
        switchMap(() =>
          roleId === value.role ? of(user) : this.adminUsersApi.updateRole(user.id, { role: value.role }),
        ),
        finalize(() => this.pendingUserId.set(null)),
        takeUntilDestroyed(this.destroyRef),
      )
      .subscribe({
        next: () => {
          this.editingUserId.set(null);
          this.loadDirectory();
        },
        error: (error: HttpErrorResponse) => {
          this.mutationError.set(this.errorMessage(error, 'Änderung fehlgeschlagen.'));
          if (profileSaved) this.loadDirectory();
        },
      });
  }

  protected changeStatus(user: AdminUserDirectorySummary): void {
    if (this.isSelf(user) || this.pendingUserId() !== null) return;
    const isActive = user.status?.id !== 2;
    const status: AdminUserStatusId = isActive ? 2 : 1;
    this.dialog
      .open(ConfirmDialog, {
        data: {
          title: isActive ? 'Benutzer deaktivieren?' : 'Benutzer aktivieren?',
          message: `${this.fullName(user)} wird ${isActive ? 'deaktiviert' : 'aktiviert'}.`,
          confirmLabel: isActive ? 'Deaktivieren' : 'Aktivieren',
        },
        restoreFocus: true,
      })
      .afterClosed()
      .pipe(filter(Boolean), take(1), takeUntilDestroyed(this.destroyRef))
      .subscribe(() => this.updateStatus(user.id, status));
  }

  protected isSelf(user: UserDirectoryRecord): boolean {
    return user.id === this.currentUser()?.id;
  }

  protected assignmentsChanged(): void {
    this.loadDirectory(false);
  }

  protected assignmentBusyChanged(userId: number, busy: boolean): void {
    this.assignmentBusyUserId.set(busy ? userId : null);
  }

  private updateStatus(userId: number, status: AdminUserStatusId): void {
    this.pendingUserId.set(userId);
    this.mutationError.set(null);
    this.adminUsersApi
      .updateStatus(userId, { status })
      .pipe(finalize(() => this.pendingUserId.set(null)), takeUntilDestroyed(this.destroyRef))
      .subscribe({
        next: () => this.loadDirectory(),
        error: (error: HttpErrorResponse) =>
          this.mutationError.set(this.errorMessage(error, 'Status konnte nicht geändert werden.')),
      });
  }

  private errorMessage(error: HttpErrorResponse, fallback: string): string {
    const detail = typeof error.error?.message === 'string' ? error.error.message : '';
    if (error.status === 403) return `Keine Berechtigung. ${detail}`.trim();
    return detail ? `${fallback} ${detail}` : fallback;
  }
}
