import {Component, OnChanges, OnDestroy, OnInit} from '@angular/core';
import {SimilarReplay, SuspiciousScore} from "../replays";
import {HttpClient} from "@angular/common/http";
import {catchError, EMPTY, finalize, Observable, Subscription} from "rxjs";
import {environment} from "../../environments/environment";
import {DecimalPipe, JsonPipe, NgForOf, NgIf, NgOptimizedImage} from "@angular/common";
import {ActivatedRoute, RouterLink} from "@angular/router";
import {UserDetails, UserQueueDetails} from "../userDetails";
import {countryCodeToFlag, formatDuration} from "../format";
import {Title} from "@angular/platform-browser";
import {RxStompService} from "../../corelib/stomp/stomp.service";
import {Message} from "@stomp/stompjs/esm6";
import {CuteLoadingComponent} from "../../corelib/components/cute-loading/cute-loading.component";
import {differenceInDays, differenceInHours} from "date-fns/fp";
import {FilterManagerService} from "../filter-manager.service";
import {UserService} from "../../corelib/service/user.service";

interface UserInfo {
  user_details: UserDetails;
  queue_details: UserQueueDetails;
  suspicious_scores: SuspiciousScore[];
  similar_replays: SimilarReplay[];
  total_scores: number;
}

interface UserQueueWebsocketPacket {
  message: string;
  data?: UserQueueDetails;
}

interface UserScoresFilter {
  sorting?: string;
}

@Component({
  selector: 'app-view-user',
  standalone: true,
  imports: [
    JsonPipe,
    DecimalPipe,
    NgForOf,
    RouterLink,
    NgIf,
    NgOptimizedImage,
    CuteLoadingComponent
  ],
  templateUrl: './view-user.component.html',
  styleUrl: './view-user.component.css'
})
export class ViewUserComponent implements OnInit, OnChanges, OnDestroy {

  isLoading = false;
  notFound = false;
  userId: string | null = null;
  userInfo: UserInfo | null = null;

  liveUserSub: Subscription | undefined;

  filterManager = new FilterManagerService<UserScoresFilter>("userScoresFilter");
  filteredScores: SuspiciousScore[] = [];

  constructor(
    private httpClient: HttpClient,
    private activatedRoute: ActivatedRoute,
    private title: Title,
    private rxStompService: RxStompService,
    public userService: UserService
  ) { }

  getUserInfo(): Observable<UserInfo> {
    const body = {
      userId: this.userId
    }
    return this.httpClient.post<UserInfo>(`${environment.apiUrl}/user-details`, body);
  }

  ngOnChanges(): void {
    this.ngOnInit();
  }

  ngOnInit(): void {
    this.notFound = false;
    this.isLoading = true;
    this.userInfo = null;

    this.filterManager.setFiltersFromLocal();

    this.activatedRoute.params.subscribe(params => {
      this.userId = params['userId'];
      if (this.userId) {
        this.loadUser();
      }
    });
  }

  private loadUser(isScoreUpdate = false) {
    this.getUserInfo().pipe(
      catchError(error => {
        this.userInfo = null;
        if (error.status == 404) {
          this.notFound = true;
        }
        return EMPTY;
      }),
      finalize(() => {
        this.isLoading = false;
      })
    ).subscribe(
      (response: UserInfo) => {
        this.notFound = false;
        this.userInfo = response;
        // Process the scores filter
        this.filteredScores = response.suspicious_scores;
        if(this.filterManager.filters.sorting) {
          let field = this.filterManager.filters.sorting.split("-")[0];
          let order = this.filterManager.filters.sorting.split("-")[1];
          //@ts-ignore
          this.sortScores(field, order);
        }
        if(!isScoreUpdate) {
          this.title.setTitle(`${this.userInfo.user_details.username}`);
          this.subscribeToUser();
        }
      }
    );
  }

  ngOnDestroy(): void {
    this.liveUserSub?.unsubscribe();
  }

  calculateTimeAgo(dateStr: string): string {
    const inputDate = new Date(dateStr);
    const now = new Date();

    if (isNaN(inputDate.getTime())) {
      return "???";
    }

    const difference = Math.abs(differenceInHours(now, inputDate));

    if (difference < 1) {
      return "recently";
    } else if (difference < 24) {
      return `${difference}h ago`;
    } else {
      const days = Math.abs(differenceInDays(now, inputDate));
      const hours = difference % 24;
      return `${days}d ${hours}h ago`;
    }
  }

  addUserToQueue(): void {
    const body = {
      userId: this.userInfo?.user_details.user_id
    }
    this.httpClient.post(`${environment.apiUrl}/user-queue`, body).subscribe(
      () => {
        this.userInfo!.queue_details.isProcessing = true;
      },
      (error) => {
        alert("Failed to add user to queue.");
      }
    )
  }

  subscribeToUser(): void {
    this.liveUserSub = this.rxStompService
      .watch(`/topic/live-user/${this.userInfo?.user_details.user_id}`)
      .subscribe((message: Message) => {
        let queueDetails: UserQueueWebsocketPacket = JSON.parse(message.body);
        if(queueDetails.message == "UPDATE_SCORES") {
          this.loadUser(true);
        } else {
          if(queueDetails.data != null) {
            this.userInfo!.queue_details = queueDetails.data;
          }
        }
      });
  }

  sortScores(field: 'pp' | 'ur' | 'beatmap_star_rating' | 'date', order: 'asc' | 'desc'): void {
    if(!this.userInfo) {
      return;
    }

    this.filterManager.filters.sorting = `${field}-${order}`
    this.filterManager.persistToLocalStorage();

    this.filteredScores = this.userInfo.suspicious_scores.sort((a: SuspiciousScore, b: SuspiciousScore): number => {
      let compareValue = 0;

      if (field === 'pp' || field === 'ur' || field === 'beatmap_star_rating') {
        compareValue = a[field] - b[field];
      } else if (field === 'date') {
        compareValue = a[field] < b[field] ? -1 : a[field] > b[field] ? 1 : 0;
      }

      return order === 'asc' ? compareValue : -compareValue;
    });
  }

  protected readonly formatDuration = formatDuration;
  protected readonly countryCodeToFlag = countryCodeToFlag;


}
