import { Injectable } from '@angular/core';
import {
  Firestore,
  doc,
  docData,
  setDoc,
  updateDoc,
  collection,
  query,
  where,
  getDocs,
} from '@angular/fire/firestore';
import { User } from '../auth/model/user.model';
import { Observable, from, of, switchMap } from 'rxjs';
import { AuthService } from '../auth/authService/auth.service';
import {
  EmailAuthProvider,
  reauthenticateWithCredential,
  updatePassword,
} from '@angular/fire/auth';

@Injectable({
  providedIn: 'root',
})
export class UserService {
  /**
   * Retrieves the current user's profile.
   * @returns An observable emitting the current user's profile.
   */
  get currentUserProfile$(): Observable<User | null> {
    return this.authService.currentUser$.pipe(
      switchMap((user) => {
        if (!user?.uid) {
          return of(null);
        }
        const ref = doc(this.firestore, 'users', user?.uid);
        return docData(ref) as Observable<User>;
      })
    );
  }

  /**
   * Constructs a new UserService.
   * @param firestore - The Angular Firestore service instance.
   * @param authService - The authentication service instance.
   * @param httpClient - The Angular HttpClient service instance.
   */
  constructor(private firestore: Firestore, private authService: AuthService) {}

  /**
   * Adds a new user to the Firestore database.
   * @param user - The user object to be added.
   * @returns An observable indicating the success or failure of the operation.
   */
  addUser(user: User): Observable<any> {
    // Ensure that the user.uid is not undefined or an empty string
    if (!user.uid) {
      throw new Error(
        'Invalid UID: UID is required to create a user document.'
      );
    }
    const ref = doc(this.firestore, 'users', user?.uid);
    return from(setDoc(ref, user));
  }

  /**
   * Updates an existing user in the Firestore database.
   * @param user - The updated user object.
   * @returns An observable indicating the success or failure of the operation.
   */
  updateUser(user: User): Observable<any> {
    const ref = doc(this.firestore, 'users', user?.uid);
    return from(updateDoc(ref, { ...user }));
  }

  // UserService method to count the user's games and update the wins field
  updateUserWins(userId: string): Observable<any> {
    const userRef = doc(this.firestore, 'users', userId);
    const gamesRef = collection(this.firestore, 'games');
    const q = query(gamesRef, where('userRef', '==', userRef));

    return from(getDocs(q)).pipe(
      switchMap((querySnapshot) => {
        // The size property indicates the number of documents in the QuerySnapshot
        const wins = querySnapshot.size;
        // Update the wins field in the user document
        return from(updateDoc(userRef, { wins: wins }));
      })
    );
  }

  updatePassword(
    currentPassword: string,
    newPassword: string
  ): Observable<any> {
    return this.authService.currentUser$.pipe(
      switchMap((user) => {
        if (!user) {
          throw new Error('No user signed in');
        }
        // Get the credential from the current password
        return from(this.getCredentialFromPassword(currentPassword)).pipe(
          switchMap((credential) => {
            // Reauthenticate user with their current password
            return from(reauthenticateWithCredential(user, credential)).pipe(
              switchMap(() => {
                // Re-authentication successful, proceed to update password
                return from(updatePassword(user, newPassword));
              })
            );
          })
        );
      })
    );
  }

  getCredentialFromPassword(currentPassword: string): Promise<any> {
    return new Promise((resolve, reject) => {
      this.getUserEmail().subscribe(
        (email: string | null) => {
          if (!email) {
            reject(new Error('User email is not available'));
          } else {
            const credential = EmailAuthProvider.credential(
              email,
              currentPassword
            );
            resolve(credential);
          }
        },
        (error) => {
          reject(error);
        }
      );
    });
  }

  getUserEmail(): Observable<string | null> {
    return this.authService.currentUser$.pipe(
      switchMap((user) => {
        if (!user?.uid) {
          return of(null);
        }
        const ref = doc(this.firestore, 'users', user.uid);
        return docData(ref).pipe(
          switchMap((userData: any) => {
            // Assuming 'email' is a field in your Firestore document
            const email: string | null = userData.email || null;
            return of(email);
          })
        );
      })
    );
  }

  getUserProfile(uid: string): Observable<User | null> {
    return new Observable<User>((observer) => {
      const usersCollection = collection(this.firestore, 'users');
      const q = query(usersCollection, where('uid', '==', uid));
      getDocs(q)
        .then((querySnapshot) => {
          querySnapshot.forEach((doc) => {
            const userData = doc.data() as User;
            observer.next(userData);
          });
          observer.complete();
        })
        .catch((error) => {
          observer.error(error);
        });
    });
  }

  async getPhotoUrl(uid: string | undefined): Promise<string | null> {
    if (!uid) {
      return null;
    }
    try {
      const filesQuery = query(
        collection(this.firestore, 'files'),
        where('id', '==', uid)
      );
      const querySnapshot = await getDocs(filesQuery);

      if (!querySnapshot.empty) {
        const fileDoc = querySnapshot.docs[0];
        return fileDoc.data()['downloadURL'] as string;
      } else {
        return null;
      }
    } catch (error) {
      console.error('Error getting image file:', error);
      throw error;
    }
  }
}
