import { InjectionToken, inject } from '@angular/core';
import { HttpInterceptorFn, HttpResponse } from '@angular/common/http';
import { finalize, tap } from 'rxjs';

export interface HttpObservation {
  method: string;
  path: string;
  status?: number;
  durationMs?: number;
}

export interface HttpObserver {
  started(observation: HttpObservation): void;
  completed(observation: HttpObservation): void;
}

export const HTTP_OBSERVER = new InjectionToken<HttpObserver>('HTTP_OBSERVER', {
  factory: () => ({ started: () => undefined, completed: () => undefined }),
});

export const HTTP_OBSERVATION_CLOCK = new InjectionToken<() => number>(
  'HTTP_OBSERVATION_CLOCK',
  { factory: () => () => performance.now() }
);

/**
 * Emits metadata-only observability hooks. Headers, bodies and query strings are intentionally
 * excluded to reduce the risk of logging credentials or personal data.
 */
export const loggingInterceptor: HttpInterceptorFn = (request, next) => {
  const observer = inject(HTTP_OBSERVER);
  const now = inject(HTTP_OBSERVATION_CLOCK);
  const base = { method: request.method, path: sanitizePath(request.url) };
  const startedAt = now();
  let status: number | undefined;

  observer.started(base);

  return next(request).pipe(
    tap({
      next: (event) => {
        if (event instanceof HttpResponse) {
          status = event.status;
        }
      },
      error: (error: unknown) => {
        if (typeof error === 'object' && error !== null && 'status' in error) {
          status = Number((error as { status: unknown }).status);
        }
      },
    }),
    finalize(() => observer.completed({ ...base, status, durationMs: now() - startedAt }))
  );
};

function sanitizePath(url: string): string {
  return url.split(/[?#]/, 1)[0];
}
