import { HttpErrorResponse } from '@angular/common/http';
import { Component, DestroyRef, EventEmitter, Input, OnChanges, Output, SimpleChanges, inject, signal } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
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, switchMap, take, tap } from 'rxjs';
import { AdminUsersApi } from '../../core/users/admin-users-api';
import {
  AdminPlayerPage,
  AdminPlayerSummary,
  AdminUserDirectorySummary,
} from '../../models/user-directory.model';
import { ConfirmDialog } from '../../shared/confirm-dialog/confirm-dialog';

@Component({
  selector: 'app-player-assignments',
  imports: [
    MatButtonModule,
    MatFormFieldModule,
    MatIconModule,
    MatInputModule,
    MatProgressSpinnerModule,
  ],
  template: `
    <section
      class="assignments"
      aria-label="Spielerzuordnungen verwalten"
      [attr.aria-busy]="pendingPlayerId() !== null ? 'true' : null"
    >
      <div class="assignments__heading">
        <div>
          <h3>Spielerzuordnungen</h3>
          <p>Spieler teamübergreifend suchen und sicher verknüpfen.</p>
        </div>
        <button mat-button type="button" [disabled]="pendingPlayerId() !== null" (click)="close()">Schließen</button>
      </div>

      <form class="assignments__search" (submit)="submitSearch(); $event.preventDefault()" role="search">
        <mat-form-field appearance="outline" subscriptSizing="dynamic">
          <mat-label>Spieler suchen</mat-label>
          <mat-icon matPrefix>search</mat-icon>
          <input
            matInput
            type="search"
            name="playerSearch"
            [value]="searchDraft"
            [disabled]="pendingPlayerId() !== null"
            (input)="searchDraft = $any($event.target).value"
          />
        </mat-form-field>
        <button mat-stroked-button type="submit" [disabled]="pendingPlayerId() !== null">Suchen</button>
      </form>

      @if (loadError()) {
        <div class="assignments__error" role="alert">
          <span>{{ loadError() }}</span>
          <button mat-button type="button" (click)="loadPlayers()">Erneut versuchen</button>
        </div>
      }
      @if (mutationError()) {
        <div class="assignments__error" role="alert">{{ mutationError() }}</div>
      }

      @if (loading()) {
        <div class="assignments__state" aria-live="polite">
          <mat-spinner diameter="30" />
          <span>Spieler werden geladen …</span>
        </div>
      } @else if (!loadError() && players()?.data?.length === 0) {
        <div class="assignments__state"><span>Keine Spieler gefunden.</span></div>
      } @else if (players()) {
        <div class="player-list">
          @for (player of players()!.data; track player.id) {
            <div class="player-row" [class.player-row--inactive]="!player.active">
              <div class="player-row__copy">
                <strong>{{ player.firstName }} {{ player.lastName }}</strong>
                <span>{{ player.team.name }} · {{ player.active ? 'Aktiv' : 'Inaktiv' }}</span>
                @if (player.currentUser) {
                  <span>Aktuell: {{ userName(player.currentUser) }}</span>
                } @else {
                  <span>Nicht verknüpft</span>
                }
              </div>
              @if (player.currentUser?.id === user.id) {
                <button
                  mat-stroked-button
                  type="button"
                  [disabled]="pendingPlayerId() !== null"
                  (click)="confirmUnlink(player)"
                >
                  {{ pendingPlayerId() === player.id ? 'Wird gelöst …' : 'Verknüpfung lösen' }}
                </button>
              } @else if (player.currentUser) {
                <button
                  mat-stroked-button
                  type="button"
                  [disabled]="pendingPlayerId() !== null"
                  (click)="confirmReassign(player)"
                >
                  {{ pendingPlayerId() === player.id ? 'Wird neu zugeordnet …' : 'Neu zuordnen' }}
                </button>
              } @else {
                <button
                  mat-flat-button
                  type="button"
                  [disabled]="pendingPlayerId() !== null"
                  (click)="assign(player)"
                >
                  {{ pendingPlayerId() === player.id ? 'Wird zugeordnet …' : 'Zuordnen' }}
                </button>
              }
            </div>
          }
        </div>
        <nav class="assignments__paging" aria-label="Spielerseiten">
          <button mat-button type="button" [disabled]="players()!.page <= 1 || loading() || pendingPlayerId() !== null" (click)="previousPage()">
            <mat-icon>chevron_left</mat-icon>Zurück
          </button>
          <span>Seite {{ players()!.page }}</span>
          <button mat-button type="button" [disabled]="!players()!.hasNextPage || loading() || pendingPlayerId() !== null" (click)="nextPage()">
            Weiter<mat-icon>chevron_right</mat-icon>
          </button>
        </nav>
      }
    </section>
  `,
  styles: `
    .assignments { padding: 18px; border: 1px solid var(--mat-sys-outline-variant); border-radius: 18px; }
    .assignments__heading { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
    h3, p { margin: 0; }
    .assignments__heading p { margin-top: 4px; color: var(--mat-sys-on-surface-variant); }
    .assignments__search { display: flex; align-items: center; gap: 10px; margin: 18px 0; }
    .assignments__search mat-form-field { flex: 1; }
    .player-list { display: grid; }
    .player-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 14px 0; border-bottom: 1px solid var(--mat-sys-outline-variant); }
    .player-row--inactive { opacity: .68; }
    .player-row__copy { display: grid; gap: 2px; min-width: 0; }
    .player-row__copy span { color: var(--mat-sys-on-surface-variant); }
    .assignments__state { min-height: 120px; display: grid; place-content: center; color: var(--mat-sys-on-surface-variant); }
    .assignments__error { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; padding: 12px; border-radius: 12px; color: var(--mat-sys-on-error-container); background: var(--mat-sys-error-container); }
    .assignments__paging { display: flex; align-items: center; justify-content: space-between; margin-top: 14px; }
    @media (max-width: 600px) {
      .assignments { padding: 16px; }
      .assignments__heading, .assignments__search, .player-row { align-items: stretch; flex-direction: column; }
      .player-row button { width: 100%; }
    }
  `,
})
export class PlayerAssignments implements OnChanges {
  private readonly adminUsersApi = inject(AdminUsersApi);
  private readonly dialog = inject(MatDialog);
  private readonly destroyRef = inject(DestroyRef);
  private readonly playerRequests = new Subject<void>();

  @Input({ required: true }) user!: AdminUserDirectorySummary;
  @Output() readonly directoryChanged = new EventEmitter<void>();
  @Output() readonly closed = new EventEmitter<void>();
  @Output() readonly busyChange = new EventEmitter<boolean>();

  protected readonly players = signal<AdminPlayerPage | null>(null);
  protected readonly loading = signal(false);
  protected readonly loadError = signal<string | null>(null);
  protected readonly mutationError = signal<string | null>(null);
  protected readonly pendingPlayerId = signal<number | null>(null);
  protected searchDraft = '';
  private search = '';
  private page = 1;
  private readonly limit = 20;

  constructor() {
    this.playerRequests
      .pipe(
        switchMap(() => {
          this.loading.set(true);
          this.loadError.set(null);
          this.players.set(null);
          return this.adminUsersApi
            .loadPlayers({
              assignment: 'all',
              page: this.page,
              limit: this.limit,
              ...(this.search ? { search: this.search } : {}),
            })
            .pipe(
              tap((players) => this.players.set(players)),
              catchError((error: HttpErrorResponse) => {
                this.loadError.set(this.loadErrorMessage(error));
                return EMPTY;
              }),
              finalize(() => this.loading.set(false)),
            );
        }),
        takeUntilDestroyed(this.destroyRef),
      )
      .subscribe();
  }

  ngOnChanges(changes: SimpleChanges): void {
    const userChange = changes['user'];
    if (!userChange) return;
    const previous = userChange.previousValue as AdminUserDirectorySummary | undefined;
    const current = userChange.currentValue as AdminUserDirectorySummary;
    if (!userChange.firstChange && previous?.id === current.id) return;
    this.searchDraft = '';
    this.search = '';
    this.page = 1;
    this.loadPlayers();
  }

  protected loadPlayers(): void {
    this.playerRequests.next();
  }

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

  protected previousPage(): void {
    if (this.page <= 1 || this.loading() || this.pendingPlayerId() !== null) return;
    this.page -= 1;
    this.loadPlayers();
  }

  protected nextPage(): void {
    if (!this.players()?.hasNextPage || this.loading() || this.pendingPlayerId() !== null) return;
    this.page += 1;
    this.loadPlayers();
  }

  protected assign(player: AdminPlayerSummary): void {
    this.runAssignment(player);
  }

  protected close(): void {
    if (this.pendingPlayerId() === null) this.closed.emit();
  }

  protected confirmUnlink(player: AdminPlayerSummary): void {
    this.confirm({
      title: 'Verknüpfung lösen?',
      message: `${player.firstName} ${player.lastName} wird von ${this.targetName()} getrennt.`,
      confirmLabel: 'Verknüpfung lösen',
    }).subscribe(() => this.runUnlink(player));
  }

  protected confirmReassign(player: AdminPlayerSummary): void {
    this.confirm({
      title: 'Spieler neu zuordnen?',
      message: `${player.firstName} ${player.lastName} ist aktuell mit ${this.userName(player.currentUser!)} verknüpft und wird ${this.targetName()} zugeordnet.`,
      confirmLabel: 'Neu zuordnen',
    }).subscribe(() => this.runAssignment(player));
  }

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

  private targetName(): string {
    return this.userName(this.user);
  }

  private confirm(data: { title: string; message: string; confirmLabel: string }) {
    return this.dialog
      .open(ConfirmDialog, { data, restoreFocus: true })
      .afterClosed()
      .pipe(filter(Boolean), take(1), takeUntilDestroyed(this.destroyRef));
  }

  private runAssignment(player: AdminPlayerSummary): void {
    if (this.pendingPlayerId() !== null) return;
    this.setPending(player.id);
    this.mutationError.set(null);
    this.adminUsersApi
      .assignPlayer(this.user.id, player.id)
      .pipe(finalize(() => this.setPending(null)), takeUntilDestroyed(this.destroyRef))
      .subscribe({
        next: () => this.refreshAfterMutation(),
        error: (error: HttpErrorResponse) => this.mutationError.set(this.mutationErrorMessage(error)),
      });
  }

  private runUnlink(player: AdminPlayerSummary): void {
    if (this.pendingPlayerId() !== null) return;
    this.setPending(player.id);
    this.mutationError.set(null);
    this.adminUsersApi
      .unlinkPlayer(this.user.id, player.id)
      .pipe(finalize(() => this.setPending(null)), takeUntilDestroyed(this.destroyRef))
      .subscribe({
        next: () => this.refreshAfterMutation(),
        error: (error: HttpErrorResponse) => this.mutationError.set(this.mutationErrorMessage(error)),
      });
  }

  private refreshAfterMutation(): void {
    this.directoryChanged.emit();
    this.loadPlayers();
  }

  private setPending(playerId: number | null): void {
    this.pendingPlayerId.set(playerId);
    this.busyChange.emit(playerId !== null);
  }

  private loadErrorMessage(error: HttpErrorResponse): string {
    const detail = typeof error.error?.message === 'string' ? error.error.message : '';
    const fallback = error.status === 403 ? 'Keine Berechtigung.' : 'Spieler konnten nicht geladen werden.';
    return detail ? `${fallback} ${detail}` : fallback;
  }

  private mutationErrorMessage(error: HttpErrorResponse): string {
    const detail = typeof error.error?.message === 'string' ? error.error.message : '';
    const fallback = error.status === 403 ? 'Keine Berechtigung.' : 'Zuordnung konnte nicht geändert werden.';
    return detail ? `${fallback} ${detail}` : fallback;
  }
}
