import { Component, DestroyRef, OnInit, inject } from '@angular/core';
import { CommonModule, TitleCasePipe } from '@angular/common';
import { Router, RouterLink } from '@angular/router';
import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { forkJoin } from 'rxjs';
import { TournamentService } from '../../services/tournament.service';
import { AuthService } from '../../services/auth.service';
import { BotService } from '../../services/bot.service';
import { OfficialBotService } from '../../services/official-bot.service';
import { TournamentServerService, ExternalTournamentServer } from '../../services/tournament-server.service';
import { Bot } from '../../models/bot.models';
import { Tournament, TournamentResult, RoundPairings, TournamentPairing } from '../../models/tournament.models';
import { CurrentUser } from '../../models/auth.models';
import { environment } from '../../../environments/environment';

type StatusTab = 'started' | 'created' | 'finished';

@Component({
  selector: 'app-tournaments',
  standalone: true,
  imports: [CommonModule, RouterLink, FormsModule, ReactiveFormsModule, TitleCasePipe],
  templateUrl: './tournaments.component.html',
  styleUrl: './tournaments.component.css'
})
export class TournamentsComponent implements OnInit {
  private readonly destroyRef = inject(DestroyRef);
  private readonly tournamentService = inject(TournamentService);
  private readonly authService = inject(AuthService);
  private readonly fb = inject(FormBuilder);
  private readonly botService = inject(BotService);
  private readonly officialBotService = inject(OfficialBotService);
  private readonly tournamentServerService = inject(TournamentServerService);
  private readonly router = inject(Router);

  loading = true;
  tab: StatusTab = 'started';
  currentUser: CurrentUser | null = null;

  started: Tournament[] = [];
  created: Tournament[] = [];
  finished: Tournament[] = [];

  selectedTournament: Tournament | null = null;
  pairings: RoundPairings | null = null;
  pairingsLoading = false;

  showCreateDialog = false;
  createForm: FormGroup = this.fb.group({
    name: ['', [Validators.required, Validators.minLength(3)]],
    nbRounds: [4, [Validators.required, Validators.min(1), Validators.max(20)]],
    clockLimitMinutes: [3, [Validators.required, Validators.min(1), Validators.max(60)]],
    clockIncrement: [0, [Validators.required, Validators.min(0), Validators.max(60)]],
    rated: [false]
  });
  createLoading = false;
  createError: string | null = null;

  startingId: string | null = null;

  joinDialogTournamentId: string | null = null;
  userBots: Bot[] = [];
  officialBots: Bot[] = [];
  botsLoading = false;
  joiningBotId: string | null = null;
  joinError: string | null = null;

  readonly officialDifficulties = ['easy', 'medium', 'hard', 'expert'] as const;
  joiningOfficialDifficulty: string | null = null;
  officialJoinError: string | null = null;

  showServersDialog = false;
  servers: ExternalTournamentServer[] = [];
  serversLoading = false;

  ngOnInit(): void {
    this.authService.currentUser$
      .pipe(takeUntilDestroyed(this.destroyRef))
      .subscribe(u => { this.currentUser = u; });
    this.loadTournaments();
    this.tournamentServerService.list()
      .pipe(takeUntilDestroyed(this.destroyRef))
      .subscribe({ next: res => { this.servers = res.servers; }, error: () => {} });
  }

  openCreateDialog(): void {
    this.createForm.reset({ name: '', nbRounds: 4, clockLimitMinutes: 3, clockIncrement: 0, rated: false });
    this.createError = null;
    this.showCreateDialog = true;
  }

  closeCreateDialog(): void {
    this.showCreateDialog = false;
  }

  submitCreate(): void {
    if (this.createForm.invalid) return;
    this.createLoading = true;
    this.createError = null;
    this.tournamentService.create(this.createForm.value).subscribe({
      next: t => {
        this.createLoading = false;
        this.showCreateDialog = false;
        this.created = [t, ...this.created];
        this.tab = 'created';
        this.selectedTournament = null;
      },
      error: err => {
        this.createLoading = false;
        this.createError = err.error?.message ?? err.error?.error ?? 'Failed to create tournament.';
      }
    });
  }

  setTab(tab: StatusTab): void {
    this.tab = tab;
    this.selectedTournament = null;
    this.pairings = null;
  }

  selectTournament(t: Tournament): void {
    if (this.selectedTournament?.id === t.id) {
      this.selectedTournament = null;
      this.pairings = null;
      return;
    }
    this.selectedTournament = t;
    this.pairings = null;
    if (t.round > 0) {
      this.loadPairings(t.id, t.round);
    }
  }

  get activeList(): Tournament[] {
    return this[this.tab];
  }

  clockDisplay(t: Tournament): string {
    const min = Math.floor(t.clock.limit / 60);
    return `${min}+${t.clock.increment}`;
  }

  rankMedal(rank: number): string {
    if (rank === 1) return '🥇';
    if (rank === 2) return '🥈';
    if (rank === 3) return '🥉';
    return `${rank}.`;
  }

  scoreDisplay(r: TournamentResult): string {
    return r.points % 1 === 0 ? `${r.points}` : `${r.points}`;
  }

  startTournament(event: MouseEvent, t: Tournament): void {
    event.stopPropagation();
    this.startingId = t.id;
    this.tournamentService.start(t.id).subscribe({
      next: updated => {
        this.startingId = null;
        const list = this.created.map(x => x.id === t.id ? updated : x);
        this.created = list.filter(x => x.status === 'created');
        if (!this.started.find(x => x.id === updated.id)) this.started = [updated, ...this.started];
        this.selectedTournament = updated;
        this.tab = 'started';
      },
      error: () => { this.startingId = null; }
    });
  }

  firstGameId(p: TournamentPairing): string | null {
    return p.matches?.[0]?.gameId ?? null;
  }

  pairingKey(p: TournamentPairing): string {
    const w = p.white?.id ?? 'bye';
    const b = p.black?.id ?? 'bye';
    return `${w}-${b}-${this.firstGameId(p) ?? ''}`;
  }

  watchGame(gameId: string): void {
    const tid = this.selectedTournament?.id;
    if (!tid) return;
    const server = this.servers[0]?.url || environment.tournamentServerUrl;
    if (!server) {
      this.joinError = 'No tournament server configured. Cannot open stream.';
      return;
    }
    void this.router.navigate(['/tournament', tid, 'game', gameId], {
      queryParams: { server }
    });
  }

  openJoinDialog(event: MouseEvent, tournamentId: string): void {
    event.stopPropagation();
    this.joinDialogTournamentId = tournamentId;
    this.joinError = null;
    this.botsLoading = true;
    forkJoin({ user: this.botService.list(), official: this.botService.listOfficial() })
      .pipe(takeUntilDestroyed(this.destroyRef))
      .subscribe({
        next: ({ user, official }) => {
          this.userBots = user;
          this.officialBots = official;
          this.botsLoading = false;
        },
        error: () => { this.botsLoading = false; }
      });
  }

  closeJoinDialog(): void {
    this.joinDialogTournamentId = null;
    this.userBots = [];
    this.officialBots = [];
    this.joiningBotId = null;
    this.joinError = null;
    this.joiningOfficialDifficulty = null;
    this.officialJoinError = null;
  }

  joinWithOfficialBot(difficulty: string): void {
    if (!this.joinDialogTournamentId || this.joiningOfficialDifficulty || this.joiningBotId) return;
    this.joiningOfficialDifficulty = difficulty;
    this.officialJoinError = null;
    const tid = this.joinDialogTournamentId;
    this.officialBotService.joinTournament(tid, difficulty).subscribe({
      next: () => {
        this.joiningOfficialDifficulty = null;
        this.closeJoinDialog();
        this.tournamentService.get(tid)
          .pipe(takeUntilDestroyed(this.destroyRef))
          .subscribe(updated => {
            this.created = this.created.map(x => x.id === tid ? updated : x);
            this.started = this.started.map(x => x.id === tid ? updated : x);
            if (this.selectedTournament?.id === tid) this.selectedTournament = updated;
          });
      },
      error: err => {
        this.joiningOfficialDifficulty = null;
        this.officialJoinError = err.error?.error ?? 'Failed to join with official bot.';
      }
    });
  }

  joinWithBot(bot: Bot): void {
    if (!this.joinDialogTournamentId || this.joiningBotId) return;
    this.joiningBotId = bot.id;
    this.joinError = null;
    this.tournamentService.join(this.joinDialogTournamentId, bot.id, bot.name).subscribe({
      next: () => {
        this.joiningBotId = null;
        const tid = this.joinDialogTournamentId!;
        this.closeJoinDialog();
        this.tournamentService.get(tid)
          .pipe(takeUntilDestroyed(this.destroyRef))
          .subscribe(updated => {
            this.created = this.created.map(x => x.id === tid ? updated : x);
            this.started = this.started.map(x => x.id === tid ? updated : x);
            if (this.selectedTournament?.id === tid) this.selectedTournament = updated;
          });
      },
      error: err => {
        this.joiningBotId = null;
        this.joinError = err.error?.message ?? err.error?.error ?? 'Failed to join tournament.';
      }
    });
  }

  openServersDialog(): void {
    this.showServersDialog = true;
    this.serversLoading = true;
    this.tournamentServerService.list()
      .pipe(takeUntilDestroyed(this.destroyRef))
      .subscribe({
        next: res => { this.servers = res.servers; this.serversLoading = false; },
        error: () => { this.serversLoading = false; }
      });
  }

  closeServersDialog(): void {
    this.showServersDialog = false;
  }

  private loadTournaments(): void {
    this.tournamentService.list()
      .pipe(takeUntilDestroyed(this.destroyRef))
      .subscribe({
        next: list => {
          this.started = list.started;
          this.created = list.created;
          this.finished = list.finished;
          this.loading = false;
          if (this.started.length === 0 && this.created.length > 0) this.tab = 'created';
          else if (this.started.length === 0 && this.finished.length > 0) this.tab = 'finished';
        },
        error: () => { this.loading = false; }
      });
  }

  private loadPairings(id: string, round: number): void {
    this.pairingsLoading = true;
    this.tournamentService.roundPairings(id, round)
      .pipe(takeUntilDestroyed(this.destroyRef))
      .subscribe({
        next: p => { this.pairings = p; this.pairingsLoading = false; },
        error: () => { this.pairingsLoading = false; }
      });
  }
}
