import {Component, OnInit, ViewChild} from '@angular/core';
import {HttpClient} from "@angular/common/http";
import {ActivatedRoute, RouterLink} from "@angular/router";
import {Title} from "@angular/platform-browser";
import {DistributionEntry, UserReplayData} from "../replays";
import {environment} from "../../environments/environment";
import {catchError, throwError} from "rxjs";
import {ChartComponent} from "../../corelib/components/chart/chart.component";
import {BaseChartDirective, NgChartsModule} from "ng2-charts";
import {DecimalPipe, NgForOf, NgIf, NgOptimizedImage} from "@angular/common";
import {calculateAccuracy} from "../format";
import {ChartConfiguration} from "chart.js";

@Component({
  selector: 'app-view-user-score',
  standalone: true,
  imports: [
    ChartComponent,
    NgChartsModule,
    NgIf,
    NgForOf,
    RouterLink,
    DecimalPipe,
    NgOptimizedImage
  ],
  templateUrl: './view-user-score.component.html',
  styleUrl: './view-user-score.component.css'
})
export class ViewUserScoreComponent implements OnInit {

  @ViewChild(BaseChartDirective)
  public chart!: BaseChartDirective;

  isLoading = false;
  error: string | null = null;
  replayData: UserReplayData | null = null;
  replayId: number | null = null;

  public barChartLegend = true;
  public barChartPlugins = [];

  public barChartData: ChartConfiguration<'bar'>['data'] = {
    labels: [],
    datasets: [
      { data: [], label: 'Miss (%)', backgroundColor: 'rgba(255,0,0,0.66)', borderRadius: 5 },
      { data: [], label: '50 (%)', backgroundColor: 'rgba(187,129,33,0.66)', borderRadius: 5 },
      { data: [], label: '100 (%)', backgroundColor: 'rgba(219,255,0,0.8)', borderRadius: 5 },
      { data: [], label: '300 (%)', backgroundColor: 'rgba(0,255,41,0.66)', borderRadius: 5 }
    ],
  };

  public barChartOptions: ChartConfiguration<'bar'>['options'] = {
    responsive: true,
    scales: {
      x: {
        stacked: true,
      },
      y: {
        stacked: true
      }
    }
  };

  constructor(private http: HttpClient,
              private activatedRoute: ActivatedRoute,
              private title: Title
  ) {}

  ngOnInit(): void {
    this.activatedRoute.params.subscribe(params => {
      this.replayId = params['replayId'];
      if (this.replayId) {
        this.loadScoreData();
      }
    });
  }

  private loadScoreData(): void {
    this.isLoading = true;
    this.http.get<UserReplayData>(`${environment.apiUrl}/user-score/${this.replayId}`).pipe(
      catchError(error => {
        this.isLoading = false;
        return throwError(() => new Error('An error occurred with the request: ' + error.message));
      })
    ).subscribe({
      next: (response) => {
        this.isLoading = false;
        this.replayData = response;

        this.title.setTitle(
          `${this.replayData.username} on ${this.replayData.beatmap_title} (${this.replayData.beatmap_version})`
        )

        let errorDistribution = Object.entries(this.replayData.error_distribution);

        if (errorDistribution.length >= 1) {
          const sortedEntries = errorDistribution
            .sort((a, b) => parseInt(a[0]) - parseInt(b[0]));

          const chartData = this.generateChartData(sortedEntries);
          this.barChartData.labels = chartData.labels;
          for (let i = 0; i < 4; i++) {
            this.barChartData.datasets[i].data = chartData.datasets[i];
          }
        }

      },
      error: (error) => {
        this.error = error;
      }
    });
  }

  private getChartRange(entries: [string, DistributionEntry][]): [number, number] {
    const keys = entries.map(([key, _]) => parseInt(key));

    const minKey = Math.min(...keys);
    const maxKey = Math.max(...keys);

    return [minKey, maxKey];
  }

  private generateChartData(entries: [string, DistributionEntry][]): { labels: string[], datasets: number[][] } {
    const range = this.getChartRange(entries);
    const labels: string[] = [];
    const datasets: number[][] = Array(4).fill(0).map(() => []);

    const defaultPercentageValues: DistributionEntry = {
      countMiss: 0,
      count50: 0,
      count100: 0,
      count300: 0,
    };

    const entriesMap = new Map<number, DistributionEntry>(entries.map(([key, value]) => [parseInt(key), value]));

    for (let key = range[0]; key <= range[1]; key += 2) {
      const endKey = key + 2 <= range[1] ? key + 2 : key + 1;
      labels.push(`${key}ms to ${endKey}ms`);

      const currentEntry = entriesMap.get(key) || { ...defaultPercentageValues };
      const nextEntry = key + 1 <= range[1] ? (entriesMap.get(key + 1) || { ...defaultPercentageValues }) : defaultPercentageValues;

      const sumEntry: DistributionEntry = {
        countMiss: currentEntry.countMiss + nextEntry.countMiss,
        count50: currentEntry.count50 + nextEntry.count50,
        count100: currentEntry.count100 + nextEntry.count100,
        count300: currentEntry.count300 + nextEntry.count300,
      };

      datasets[0].push(sumEntry.countMiss);
      datasets[1].push(sumEntry.count50);
      datasets[2].push(sumEntry.count100);
      datasets[3].push(sumEntry.count300);
    }

    // Handling the case for an odd last key if needed
    if (range[1] % 2 !== range[0] % 2) {
      const lastEntry = entriesMap.get(range[1]) || { ...defaultPercentageValues };
      labels.push(`${range[1]}ms to ${range[1] + 1}ms`);
      datasets[0].push(lastEntry.countMiss);
      datasets[1].push(lastEntry.count50);
      datasets[2].push(lastEntry.count100);
      datasets[3].push(lastEntry.count300);
    }

    return { labels, datasets };
  }

  protected readonly Object = Object;
  protected readonly calculateAccuracy = calculateAccuracy;
}
