import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { isPlatformBrowser } from '@angular/common';
import { computed, inject, Injectable, PLATFORM_ID, signal } from '@angular/core';
import { firstValueFrom, map, Observable, of, tap } from 'rxjs';

import { environment } from '../../../../environments/environment';
import {
  AuthUser,
  LoginPayload,
  LoginResponse,
  RegisterPayload,
  RegisterResponse
} from './auth.interfaces';

const AUTH_TOKEN_STORAGE_KEY = 'shopit.auth.token';

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  private readonly http = inject(HttpClient);
  private readonly platformId = inject(PLATFORM_ID);

  private readonly userState = signal<AuthUser | null>(null);
  private readonly tokenState = signal<string | null>(null);

  readonly user = this.userState.asReadonly();
  readonly token = this.tokenState.asReadonly();
  readonly isAuthenticated = computed(() => this.tokenState() !== null);

  login(payload: LoginPayload): Observable<AuthUser> {
    return this.http.post<LoginResponse>(`${environment.url}login`, payload).pipe(
      tap((response) => this.applyAuthenticatedState(response.token, response.user)),
      map((response) => response.user)
    );
  }

  register(payload: RegisterPayload): Observable<RegisterResponse> {
    return this.http.post<RegisterResponse>(`${environment.url}register`, payload);
  }

  logout(): Observable<void> {
    if (!this.tokenState()) {
      this.clearSession();
      return of(void 0);
    }

    return this.http.post<void>(`${environment.url}logout`, {}).pipe(
      tap({
        next: () => this.clearSession(),
        error: () => this.clearSession()
      })
    );
  }

  async bootstrap(): Promise<void> {
    this.hydrateSession();

    if (!this.tokenState() || this.userState()) {
      return;
    }

    try {
      await firstValueFrom(this.loadCurrentUser());
    } catch (error) {
      if (this.isUnauthorizedError(error)) {
        this.clearSession();
        return;
      }

      throw error;
    }
  }

  loadCurrentUser(): Observable<AuthUser> {
    return this.http.get<AuthUser>(`${environment.url}me`).pipe(
      tap((user) => this.userState.set(user))
    );
  }

  hydrateSession(): void {
    if (!isPlatformBrowser(this.platformId)) {
      return;
    }

    const token = window.localStorage.getItem(AUTH_TOKEN_STORAGE_KEY);
    this.tokenState.set(token);
  }

  clearSession(): void {
    this.userState.set(null);
    this.tokenState.set(null);

    if (!isPlatformBrowser(this.platformId)) {
      return;
    }

    window.localStorage.removeItem(AUTH_TOKEN_STORAGE_KEY);
  }

  private applyAuthenticatedState(token: string, user: AuthUser): void {
    this.tokenState.set(token);
    this.userState.set(user);

    if (!isPlatformBrowser(this.platformId)) {
      return;
    }

    window.localStorage.setItem(AUTH_TOKEN_STORAGE_KEY, token);
  }

  private isUnauthorizedError(error: unknown): error is HttpErrorResponse {
    return error instanceof HttpErrorResponse && error.status === 401;
  }
}
