import { Injectable, OnDestroy, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, BehaviorSubject, ReplaySubject, Subscription } from 'rxjs';
import { distinctUntilChanged, take, tap } from 'rxjs/operators';

import { User } from '@models';
import { JwtService } from '@services';

@Injectable({ providedIn: 'root' })
export class UserService implements OnDestroy {
    private readonly http = inject(HttpClient);
    private readonly jwtService = inject(JwtService);

    private _subcriptions: Array<Subscription> = new Array<Subscription>();
    private _user: Subscription = new Subscription();
    private currentUserSubject = new BehaviorSubject<User>({} as User);
    public currentUser = this.currentUserSubject.asObservable().pipe(distinctUntilChanged());

    private isAuthenticatedSubject = new ReplaySubject<boolean>(1);
    public isAuthenticated = this.isAuthenticatedSubject.asObservable();

    private _apiDomain = '/cms';

    ngOnDestroy() {
        this._subcriptions.forEach((subscription: Subscription) => subscription.unsubscribe());
    }

    // Verify JWT in localstorage with server & load user's info.
    // This runs once on application startup.
    populate() {
        // If JWT detected, attempt to get & store user's info
        if (this.jwtService.getToken()) {
            const user$: Observable<{ user: User }> = this.http
                .get<{ user: User }>(`${this._apiDomain}/user`)
                .pipe(take(1));
            this._user = user$.subscribe({
                next: (data) => this.setAuth(data.user),
                error: () => this.purgeAuth(),
            });
            this._subcriptions.push(this._user);
        } else {
            // Remove any potential remnants of previous auth states
            this.purgeAuth();
        }
    }

    setAuth(user: User) {
        // Save JWT sent from server in localstorage
        this.jwtService.saveToken(user.token);
        // Set current user data into observable
        this.currentUserSubject.next(user);
        // Set isAuthenticated to true
        this.isAuthenticatedSubject.next(true);
    }

    purgeAuth() {
        // Remove JWT from localstorage
        this.jwtService.destroyToken();
        // Set current user to an empty object
        this.currentUserSubject.next({} as User);
        // Set auth status to false
        this.isAuthenticatedSubject.next(false);
    }

    attemptAuth(type: string, credentials?: { email: string; password: string }): Observable<{ user: User }> {
        const route = type === 'login' ? '/login' : '';
        const user$: Observable<{ user: User }> = this.http.post<{ user: User }>(`${this._apiDomain}/user${route}`, {
            user: credentials,
        });
        /*return user$.pipe(map(
            (data: any) => {
                this.setAuth(data.user);
                return data;
            }
        ));*/
        return user$.pipe(tap(({ user }) => this.setAuth(user)));
    }

    getCurrentUser(): User {
        return this.currentUserSubject.value;
    }

    canAdministrate(): boolean {
        return this.currentUserSubject.value.role == 'Admin' ? true : false;
    }

    // Update the user on the server (email, pass, etc)
    update(user: Partial<User>): Observable<{ user: User }> {
        return this.http.put<{ user: User }>(`${this._apiDomain}/user`, { user }).pipe(
            tap(({ user }) => {
                this.currentUserSubject.next(user);
            }),
        );
    }
}
