import { HttpInterceptorFn, HttpErrorResponse, HttpRequest } from '@angular/common/http';
import { inject } from '@angular/core';
import { catchError, switchMap, throwError } from 'rxjs';
import { AuthService } from '../../shared/services/auth.service';
import { Router } from '@angular/router';
import { ApiConfigService } from '../services/api-config.service';

/**
 * Attaches Bearer tokens to ROLAC API calls and silently refreshes on 401.
 */
export const authInterceptor: HttpInterceptorFn = (req, next) => {
    const authService = inject(AuthService);
    const apiConfig   = inject(ApiConfigService);
    const router      = inject(Router);

    const request = attachToken(req, authService, apiConfig);

    return next(request).pipe(
        catchError((error: HttpErrorResponse) => {
            if (
                error.status === 401 &&
                !req.headers.has('X-Retry') &&
                !isPublicAuthRequest(req, apiConfig)
            ) {
                return authService.refresh().pipe(
                    switchMap(success => {
                        if (success) {
                            const retryReq = attachToken(
                                req.clone({ setHeaders: { 'X-Retry': 'true' } }),
                                authService,
                                apiConfig
                            );
                            return next(retryReq);
                        }
                        authService.logout();
                        router.navigate(['/login']);
                        return throwError(() => error);
                    })
                );
            }

            // Redirect on a genuine auth failure, but NOT for the public auth endpoints
            // (login / refresh / logout). The startup session-restore POSTs /auth/refresh on
            // every page load; for an unauthenticated visitor that 401s, and bouncing them to
            // /login here would break public routes like /attendance. refresh() handles its own 401.
            if (error.status === 401 && !isPublicAuthRequest(req, apiConfig)) {
                authService.logout();
                router.navigate(['/login']);
            }

            return throwError(() => error);
        })
    );
};

function attachToken(
    req: HttpRequest<unknown>,
    authService: AuthService,
    apiConfig: ApiConfigService
): HttpRequest<unknown> {
    const token = authService.getToken();
    if (!token || !shouldAddToken(apiConfig, req)) {
        return req;
    }
    return req.clone({ setHeaders: { Authorization: `Bearer ${token}` } });
}

const PUBLIC_AUTH_PATHS = ['/auth/login', '/auth/refresh', '/auth/logout'];

function isPublicAuthRequest(req: HttpRequest<unknown>, apiConfig: ApiConfigService): boolean {
    if (!req.url.startsWith(apiConfig.getBaseUrl())) {
        return false;
    }
    return PUBLIC_AUTH_PATHS.some(path => req.url.toLowerCase().includes(path));
}

function shouldAddToken(apiConfig: ApiConfigService, req: HttpRequest<unknown>): boolean {
    return !isPublicAuthRequest(req, apiConfig);
}
