import {
  Injectable,
  NestInterceptor,
  ExecutionContext,
  CallHandler,
  UnauthorizedException,
} from "@nestjs/common";
import { Observable, defer } from "rxjs";
import { tap, catchError } from "rxjs/operators";
import {
  FailedLoginThrottleGuard,
  type RequestWithRateLimit,
} from "@bff/modules/auth/presentation/http/guards/failed-login-throttle.guard";
import type { Request, Response } from "express";

@Injectable()
export class LoginResultInterceptor implements NestInterceptor {
  constructor(private readonly failedLoginGuard: FailedLoginThrottleGuard) {}

  intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
    const rawRequest = context.switchToHttp().getRequest<unknown>();
    const response = context.switchToHttp().getResponse<Response>();
    if (!this.isExpressRequest(rawRequest)) {
      throw new UnauthorizedException("Invalid request context");
    }
    const request: RequestWithRateLimit = rawRequest as RequestWithRateLimit;

    return next.handle().pipe(
      tap(() => {
        void this.failedLoginGuard.handleLoginResult(request, true);
        FailedLoginThrottleGuard.applyRateLimitHeaders(request, response);
      }),
      catchError(error =>
        defer(async () => {
          const message = this.extractErrorMessage(error).toLowerCase();
          const status = this.extractStatusCode(error);
          const isAuthError =
            error instanceof UnauthorizedException ||
            status === 401 ||
            message.includes("invalid") ||
            message.includes("unauthorized");

          if (isAuthError) {
            await this.failedLoginGuard.handleLoginResult(request, false);
            FailedLoginThrottleGuard.applyRateLimitHeaders(request, response);
          }

          throw error;
        })
      )
    );
  }

  private isExpressRequest(value: unknown): value is Request {
    if (!value || typeof value !== "object") {
      return false;
    }
    const candidate = value as Partial<Request> & {
      headers?: unknown;
      method?: unknown;
      url?: unknown;
    };
    return (
      typeof candidate.headers === "object" &&
      candidate.headers !== null &&
      typeof candidate.method === "string" &&
      typeof candidate.url === "string"
    );
  }

  private extractErrorMessage(error: unknown): string {
    if (typeof error === "string") {
      return error;
    }
    if (error && typeof error === "object" && "message" in error) {
      const maybeMessage = (error as { message?: unknown }).message;
      if (typeof maybeMessage === "string") {
        return maybeMessage;
      }
    }
    return "";
  }

  private extractStatusCode(error: unknown): number | undefined {
    if (error && typeof error === "object" && "status" in error) {
      const statusValue = (error as { status?: unknown }).status;
      if (typeof statusValue === "number") {
        return statusValue;
      }
    }
    return undefined;
  }
}
