import { inject } from '@angular/core';
import { toObservable } from '@angular/core/rxjs-interop';
import type { CanActivateFn } from '@angular/router';
import { filter, firstValueFrom, map } from 'rxjs';
import { AuthService } from './auth.service';

/**
 * Functional route guard that gates a route on the SPA-side
 * authentication state held by {@link AuthService}.
 *
 * Behaviour by `AuthState`:
 *   - `loading` — block navigation until the first `/me` round-trip
 *     resolves, then re-evaluate. Avoids the "guard returns false on
 *     a brand-new tab" footgun while the bootstrap fetch is still in
 *     flight.
 *   - `authenticated` — allow.
 *   - `anonymous` — kick off `auth.login()` (full-page redirect to
 *     the BFF's `/auth/login`, which 302s through Entra) and deny
 *     the navigation. The browser leaves the SPA before the deny
 *     surfaces UI-side.
 *   - `error` — same redirect to `/login` as `anonymous`. The user
 *     re-attempts auth; if Redis / Entra is truly down, the flow
 *     fails again with a clearer surface. Deliberate: a "can't
 *     reach the server" page on a protected route is less useful
 *     than the BFF-side login screen's diagnostics.
 *
 * Usage in `app.routes.ts`:
 *
 *   { path: 'profile', canActivate: [authGuard], loadComponent: ... }
 */
export const authGuard: CanActivateFn = async () => {
  const auth = inject(AuthService);

  // Wait out the bootstrap fetch. After that, `state` is guaranteed
  // to be one of the three terminal kinds.
  const settled = await firstValueFrom(
    toObservable(auth.state).pipe(
      filter((s) => s.kind !== 'loading'),
      map((s) => s),
    ),
  );

  if (settled.kind === 'authenticated') {
    return true;
  }

  auth.login();
  return false;
};
