import { HttpClient } from '@angular/common/http';
import { ChangeDetectionStrategy, Component, inject, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { AuthenticationService, TokenStore } from '@aurora';
import { catchError, of, switchMap } from 'rxjs';
import { environment } from 'environments/environment';
import { AUTH_STRATEGY, type AuthStrategy } from '../../../../environments/environment.type';

/**
 * Returns the Angular router path the app should navigate to after a local sign-out,
 * or `null` when the component must perform a top-level BFF navigation instead.
 *
 * Exported so unit tests can assert the correct destination for each strategy
 * without mounting the full component. Inverting the condition in this function
 * will immediately break those tests.
 *
 *   - AUTH_STRATEGY.PASSWORD            → '/auth/sign-in' (local login form).
 *   - AUTH_STRATEGY.AUTHORIZATION_CODE  → null (component navigates to the BFF
 *                                         via window.location.href — RP-initiated
 *                                         logout, D2/D8).
 */
export const signOutDestinationForStrategy = (strategy: AuthStrategy): string | null => {
  if (strategy === AUTH_STRATEGY.AUTHORIZATION_CODE) {
    // Under AUTHORIZATION_CODE the hub session must be terminated by navigating
    // to the satellite's OWN BFF logout endpoint (GET /auth/logout), which
    // issues a 302 to the hub. The frontend must NOT know the hub URL — only
    // the BFF does (D2). Returning null signals that window.location.href is
    // used instead of Angular Router.
    return null;
  }

  return '/auth/sign-in';
};

/**
 * Builds the BFF logout URL that the satellite navigates to for RP-initiated logout.
 *
 * Per design decision D2/D8, the frontend navigates to its OWN origin BFF endpoint
 * (`GET ${bffRestBase}/auth/logout`), NOT directly to the hub. The BFF owns the hub
 * URL and issues the 302 redirect to the hub — symmetric to how login works:
 * the frontend navigates to `${bffRestBase}/auth/login` and the BFF redirects to
 * the hub's `/authorize`.
 *
 * No query parameters are added by the frontend — the BFF constructs `client_id`,
 * `post_logout_redirect_uri`, and `state` from its own backend config.
 *
 * Exported for unit testing.
 *
 * @param bffRestBase - The satellite BFF REST base URL (`environment.api.rest`).
 * @returns The full BFF logout URL to assign to `window.location.href`.
 */
export const buildBffLogoutUrl = (bffRestBase: string): string => {
  return `${bffRestBase}/auth/logout`;
};

@Component({
  selector: 'app-sign-out',
  changeDetection: ChangeDetectionStrategy.OnPush,
  host: { class: 'block' },
  template: `
    <div class="text-muted-foreground flex min-h-svh items-center justify-center text-sm">
      Signing out…
    </div>
  `,
})
export default class SignOutComponent implements OnInit {
  private readonly authenticationService = inject(AuthenticationService);
  private readonly router = inject(Router);
  // TokenStore is injected in @app (not @aurora) so that the hub revocation HTTP call
  // lives here (D6 / HR-PORTS-001): the @aurora adapter stays framework-agnostic and
  // only handles GraphQL revocation + tokenStore.clear(). The @app sign-out layer reads
  // the refresh token before signOut() clears it, then calls POST /auth/logout (BFF
  // revocation) as a best-effort step.
  private readonly tokenStore = inject(TokenStore);
  private readonly http = inject(HttpClient);

  ngOnInit(): void {
    // Under AUTHORIZATION_CODE (D6 / Task 6.2):
    //   1. Read the refresh token BEFORE signOut() clears the store.
    //   2. Call POST /auth/logout with the token (best-effort hub revocation via BFF).
    //      A failure MUST NOT block the rest of the flow.
    //   3. Call signOut() to revoke the GraphQL session + clear local credentials.
    //   4. Navigate to the BFF logout endpoint (GET /auth/logout) for hub-session termination.
    //
    // Under PASSWORD: proceed directly to signOut() + Angular Router navigation.
    //
    // The frontend MUST NOT navigate directly to the hub — the BFF owns the hub URL
    // and constructs the full hub redirect including client_id, post_logout_redirect_uri,
    // and state (D2). This is symmetric to how login works.

    const strategy = environment.authStrategy;

    if (strategy === AUTH_STRATEGY.AUTHORIZATION_CODE) {
      // Step 1: read the refresh token synchronously BEFORE signOut() clears it.
      const refreshToken = this.tokenStore.refreshToken();

      // Step 2: best-effort hub token revocation via BFF.
      // The BFF POST /auth/logout performs a server-to-server revocation of the hub
      // refresh token (RFC 7009). Its failure (network error, hub down) must not block
      // the sign-out flow — it is defense-in-depth, not a gate.
      const revokeUrl = `${environment.api.rest}/auth/logout`;
      const revoke$ = this.http
        .post(revokeUrl, { refreshToken }, { withCredentials: true })
        .pipe(catchError(() => of(null)));

      // Steps 3 + 4: after best-effort revocation (regardless of outcome), signOut()
      // clears local credentials and then we navigate to the BFF GET /auth/logout
      // to terminate the hub session.
      revoke$
        .pipe(switchMap(() => this.authenticationService.signOut()))
        .subscribe(() => {
          window.location.href = buildBffLogoutUrl(environment.api.rest);
        });

      return;
    }

    // PASSWORD strategy: signOut() revokes the satellite token + clears local credentials,
    // then navigate via Angular Router to the local login form.
    this.authenticationService.signOut().subscribe(() => {
      this.router.navigate(['/auth/sign-in']);
    });
  }
}
