import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse } from '@angular/common/http';
import { Observable, throwError, BehaviorSubject } from 'rxjs';
import { catchError, switchMap, filter, take } from 'rxjs/operators';
import { TokenService } from '../services/token.service';
import { AuthHttpService } from '../services/auth-http.service';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  private isRefreshing = false;
  private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);

  constructor(
    private tokenService: TokenService,
    private authHttpService: AuthHttpService
  ) {}

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    // Add auth token to request if available and not already present
    if (this.shouldAddToken(request)) {
      request = this.addToken(request);
    }

    return next.handle(request).pipe(
      catchError(error => {
        if (error instanceof HttpErrorResponse && error.status === 401) {
          return this.handle401Error(request, next);
        }
        return throwError(() => error);
      })
    );
  }

  /**
   * Check if token should be added to this request
   */
  private shouldAddToken(request: HttpRequest<any>): boolean {
    // Don't add token if already present
    if (request.headers.has('Authorization')) {
      return false;
    }

    // Don't add token to auth endpoints that don't require it
    const url = request.url.toLowerCase();
    const publicEndpoints = [
      '/auth/login',
      '/auth/register', 
      '/auth/refresh',
      '/auth/validate',
      '/users/forgot-password',
      '/users/reset-password',
      '/users/verify-email',
      '/oauth/providers',
      '/oauth/',
      '/health'
    ];

    return !publicEndpoints.some(endpoint => url.includes(endpoint));
  }

  /**
   * Add authentication token to request
   */
  private addToken(request: HttpRequest<any>): HttpRequest<any> {
    const authHeader = this.tokenService.getAuthorizationHeader();
    
    if (authHeader) {
      return request.clone({
        setHeaders: {
          Authorization: authHeader
        }
      });
    }

    return request;
  }

  /**
   * Handle 401 errors by attempting token refresh
   */
  private handle401Error(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (!this.isRefreshing) {
      this.isRefreshing = true;
      this.refreshTokenSubject.next(null);

      const refreshToken = this.tokenService.getRefreshToken();
      
      if (refreshToken) {
        return this.authHttpService.refreshToken({ refresh_token: refreshToken }).pipe(
          switchMap((tokenPair) => {
            this.isRefreshing = false;
            this.tokenService.setTokens(tokenPair);
            this.refreshTokenSubject.next(tokenPair.access_token);
            
            // Retry original request with new token
            return next.handle(this.addToken(request));
          }),
          catchError((error) => {
            this.isRefreshing = false;
            this.tokenService.clearTokens();
            return throwError(() => error);
          })
        );
      } else {
        this.isRefreshing = false;
        this.tokenService.clearTokens();
        return throwError(() => new Error('No refresh token available'));
      }
    } else {
      // If refresh is in progress, wait for it to complete
      return this.refreshTokenSubject.pipe(
        filter(token => token != null),
        take(1),
        switchMap(() => next.handle(this.addToken(request)))
      );
    }
  }
}