import {
  effect,
  EnvironmentInjector,
  inject,
  Injectable,
  InjectionToken,
  makeEnvironmentProviders,
  runInInjectionContext,
  type EnvironmentProviders,
} from '@angular/core';
import { TokenStore } from './token-store.service';

/**
 * Callbacks registered via {@link provideAuthenticatedInitializer}. They run once each time
 * authentication is established — i.e. on a successful sign-in AND when a valid session already
 * exists on app load (direct navigation / refresh to an authenticated URL). This is the analogue
 * of Angular's `provideAppInitializer`, but gated on authentication instead of app bootstrap.
 */
export const AUTHENTICATED_INITIALIZER = new InjectionToken<ReadonlyArray<() => void>>(
  'AUTHENTICATED_INITIALIZER',
);

/**
 * Register a callback to run once whenever the user becomes authenticated (each sign-in, and once
 * at load when a session is already valid). Mirrors `provideAppInitializer`; the callback runs in
 * an injection context, so it may use `inject()`.
 *
 * @example
 * provideAuthenticatedInitializer(() => inject(CurrentAccountService).load());
 */
export function provideAuthenticatedInitializer(callback: () => void): EnvironmentProviders {
  return makeEnvironmentProviders([
    { provide: AUTHENTICATED_INITIALIZER, useValue: callback, multi: true },
  ]);
}

/**
 * Fires the registered {@link AUTHENTICATED_INITIALIZER} callbacks on each `false → true` edge of
 * `TokenStore.authenticated()`, and re-arms on sign-out so the next sign-in runs them again. The
 * `authenticated` signal hydrates synchronously from localStorage, so a refresh / deep-link into
 * an authenticated URL also triggers them once. Bootstrapped by `provideAuthentication()`.
 */
@Injectable({ providedIn: 'root' })
export class AuthenticatedInitializerRunner {
  constructor() {
    const tokenStore = inject(TokenStore);
    const injector = inject(EnvironmentInjector);
    const initializers = inject(AUTHENTICATED_INITIALIZER, { optional: true }) ?? [];
    let armed = true;

    effect(() => {
      if (tokenStore.authenticated()) {
        if (armed) {
          armed = false;
          initializers.forEach((callback) => runInInjectionContext(injector, callback));
        }
      } else {
        armed = true;
      }
    });
  }
}
