import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { PaymentMethod } from '@stripe/stripe-js';
import { catchError, forkJoin, lastValueFrom, map, Observable, of, Subject } from 'rxjs';
import urlcat from 'urlcat';
import { User } from '../../../../bizmatch-server/src/models/db.model';
import { CombinedUser, FirebaseUserInfo, KeycloakUser, ResponseUsersArray, StripeSubscription, StripeUser, UserListingCriteria, UserRole, UsersResponse } from '../../../../bizmatch-server/src/models/main.model';
import { environment } from '../../environments/environment';

@Injectable({
  providedIn: 'root',
})
export class UserService {
  private apiBaseUrl = environment.apiBaseUrl;

  private userSource = new Subject<User>();
  currentUser = this.userSource.asObservable();

  constructor(private http: HttpClient) {}

  changeUser(user: User) {
    this.userSource.next(user);
  }
  // -----------------------------
  // DB services
  // -----------------------------
  async save(user: User): Promise<User> {
    return await lastValueFrom(this.http.post<User>(`${this.apiBaseUrl}/bizmatch/user`, user));
  }
  async saveGuaranteed(user: User): Promise<User> {
    return await lastValueFrom(this.http.post<User>(`${this.apiBaseUrl}/bizmatch/user/guaranteed`, user));
  }
  async getById(id: string): Promise<User> {
    return await lastValueFrom(this.http.get<User>(`${this.apiBaseUrl}/bizmatch/user/${id}`));
  }
  async getByMail(mail: string, hideLoading: boolean = true): Promise<User> {
    const url = urlcat(`${this.apiBaseUrl}/bizmatch/user`, { mail });
    let headers = new HttpHeaders();
    if (hideLoading) {
      headers = headers.set('X-Hide-Loading', 'true');
    }
    return await lastValueFrom(this.http.get<User>(url, { headers }));
  }
  async search(criteria?: UserListingCriteria): Promise<ResponseUsersArray> {
    return await lastValueFrom(this.http.post<ResponseUsersArray>(`${this.apiBaseUrl}/bizmatch/user/search`, criteria));
  }
  getNumberOfBroker(criteria?: UserListingCriteria): Observable<number> {
    return this.http.post<number>(`${this.apiBaseUrl}/bizmatch/user/findTotal`, criteria);
  }
  getKeycloakUser(id: string): Promise<KeycloakUser> {
    return lastValueFrom(this.http.get<KeycloakUser>(`${this.apiBaseUrl}/bizmatch/auth/users/${id}`));
  }
  async updateKeycloakUser(keycloakUser: KeycloakUser): Promise<void> {
    await lastValueFrom(this.http.put<void>(`${this.apiBaseUrl}/bizmatch/auth/users/${keycloakUser.id}`, keycloakUser));
  }
  // -------------------------------
  // ADMIN SERVICES
  // -------------------------------
  /**
   * Ruft alle Benutzer mit Paginierung ab
   */
  getAllUsers(maxResults?: number, pageToken?: string): Observable<UsersResponse> {
    let params = new HttpParams();

    if (maxResults) {
      params = params.set('maxResults', maxResults.toString());
    }

    if (pageToken) {
      params = params.set('pageToken', pageToken);
    }

    return this.http.get<UsersResponse>(`${this.apiBaseUrl}/bizmatch/auth`, { params });
  }

  /**
   * Ruft Benutzer mit einer bestimmten Rolle ab
   */
  getUsersByRole(role: UserRole): Observable<{ users: FirebaseUserInfo[] }> {
    return this.http.get<{ users: FirebaseUserInfo[] }>(`${this.apiBaseUrl}/bizmatch/auth/role/${role}`);
  }

  /**
   * Ändert die Rolle eines Benutzers
   */
  setUserRole(uid: string, role: UserRole): Observable<{ success: boolean }> {
    return this.http.post<{ success: boolean }>(`${this.apiBaseUrl}/bizmatch/auth/${uid}/role`, { role });
  }

  // -------------------------------
  // OLDADMIN SERVICES
  // -------------------------------

  getKeycloakUsers(): Observable<KeycloakUser[]> {
    return this.http.get<KeycloakUser[]>(`${this.apiBaseUrl}/bizmatch/auth/user/all`).pipe(
      catchError(error => {
        console.error('Fehler beim Laden der Keycloak-Benutzer', error);
        return of([]);
      }),
    );
  }

  getAppUsers(): Observable<User[]> {
    return this.http.get<User[]>(`${this.apiBaseUrl}/bizmatch/user/user/all`).pipe(
      catchError(error => {
        console.error('Fehler beim Laden der App-Benutzer', error);
        return of([]);
      }),
    );
  }

  getAllStripeSubscriptions(): Observable<StripeSubscription[]> {
    return this.http.get<StripeSubscription[]>(`${this.apiBaseUrl}/bizmatch/payment/subscription/all`).pipe(
      catchError(error => {
        console.error('Fehler beim Laden der Stripe-Subscriptions', error);
        return of([]);
      }),
    );
  }

  getAllStripeUsers(): Observable<StripeUser[]> {
    return this.http.get<StripeUser[]>(`${this.apiBaseUrl}/bizmatch/payment/user/all`).pipe(
      catchError(error => {
        console.error('Fehler beim Laden der Stripe-Benutzer', error);
        return of([]);
      }),
    );
  }
  getPaymentMethods(email: string): Observable<PaymentMethod[]> {
    return this.http.get<PaymentMethod[]>(`${this.apiBaseUrl}/bizmatch/payment/paymentmethod/${email}`).pipe(
      catchError(error => {
        console.error('Fehler beim Laden der Zahlungsinformationen', error);
        return of([]);
      }),
    );
  }
  /**
   * Lädt alle Benutzer aus den verschiedenen Quellen und kombiniert sie.
   * @returns Ein Observable mit einer Liste von CombinedUser.
   */
  loadUsers(): Observable<CombinedUser[]> {
    return forkJoin({
      keycloakUsers: this.getKeycloakUsers(),
      appUsers: this.getAppUsers(),
      stripeSubscriptions: this.getAllStripeSubscriptions(),
      stripeUsers: this.getAllStripeUsers(),
    }).pipe(
      map(({ keycloakUsers, appUsers, stripeSubscriptions, stripeUsers }) => {
        const combinedUsers: CombinedUser[] = [];

        // Map App Users mit Keycloak und Stripe Subscription
        appUsers.forEach(appUser => {
          const keycloakUser = keycloakUsers.find(kcUser => kcUser.email.toLowerCase() === appUser.email.toLowerCase());

          // const stripeSubscription = appUser.subscriptionId ? stripeSubscriptions.find(sub => sub.id === appUser.subscriptionId) : null;
          const stripeUser = stripeUsers.find(suser => suser.email === appUser.email);
          const stripeSubscription = stripeUser ? stripeSubscriptions.find(sub => sub.customer === stripeUser.id) : null;
          combinedUsers.push({
            appUser,
            keycloakUser,
            stripeUser,
            stripeSubscription,
          });
        });

        // Füge Stripe-Benutzer hinzu, die nicht in App oder Keycloak vorhanden sind
        stripeUsers.forEach(stripeUser => {
          const existsInApp = appUsers.some(appUser => appUser.email.toLowerCase() === stripeUser.email.toLowerCase());
          const existsInKeycloak = keycloakUsers.some(kcUser => kcUser.email.toLowerCase() === stripeUser.email.toLowerCase());

          if (!existsInApp && !existsInKeycloak) {
            combinedUsers.push({
              stripeUser,
              // Optional: Verknüpfe Stripe-Benutzer mit ihren Subscriptions
              stripeSubscription: stripeSubscriptions.find(sub => sub.customer === stripeUser.id) || null,
            });
          }
        });

        return combinedUsers;
      }),
      catchError(err => {
        console.error('Fehler beim Kombinieren der Benutzer', err);
        return of([]);
      }),
    );
  }
  async deleteCustomerFromStripe(customerId: string): Promise<void> {
    await lastValueFrom(this.http.delete(`${this.apiBaseUrl}/bizmatch/payment/customer/${customerId}`));
  }
}
