import { Injectable, UnauthorizedException } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
import { ExtractJwt, Strategy } from "passport-jwt";
import { ConfigService } from "@nestjs/config";
import type { UserAuth } from "@customer-portal/domain/customer";
import { UsersFacade } from "@bff/modules/users/application/users.facade";
import { mapPrismaUserToDomain } from "@bff/infra/mappers";
import type { Request } from "express";

const cookieExtractor = (req: Request): string | null => {
  const cookieSource: unknown = Reflect.get(req, "cookies");
  if (!cookieSource || typeof cookieSource !== "object") {
    return null;
  }
  const token = Reflect.get(cookieSource, "access_token") as unknown;
  return typeof token === "string" && token.length > 0 ? token : null;
};

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(
    private configService: ConfigService,
    private readonly usersFacade: UsersFacade
  ) {
    const jwtSecret = configService.get<string>("JWT_SECRET");
    if (!jwtSecret) {
      throw new Error("JWT_SECRET is required in environment variables");
    }

    const options = {
      jwtFromRequest: ExtractJwt.fromExtractors([
        ExtractJwt.fromAuthHeaderAsBearerToken(),
        cookieExtractor,
      ]),
      ignoreExpiration: false,
      secretOrKey: jwtSecret,
    };

    super(options);
  }

  async validate(payload: {
    sub: string;
    email: string;
    role: string;
    iat?: number;
    exp?: number;
  }): Promise<UserAuth> {
    // Validate payload structure
    if (!payload.sub || !payload.email) {
      throw new UnauthorizedException("Invalid JWT payload");
    }

    // Explicit expiry check with 60-second buffer to prevent edge cases
    // where tokens expire during request processing
    if (payload.exp) {
      const nowSeconds = Math.floor(Date.now() / 1000);
      const bufferSeconds = 60; // 1 minute buffer

      if (payload.exp < nowSeconds + bufferSeconds) {
        throw new UnauthorizedException("Token expired or expiring soon");
      }
    } else {
      // Tokens without expiry are not allowed
      throw new UnauthorizedException("Token missing expiration claim");
    }

    const prismaUser = await this.usersFacade.findByIdInternal(payload.sub);

    if (!prismaUser) {
      throw new UnauthorizedException("User not found");
    }

    if (prismaUser.email !== payload.email) {
      throw new UnauthorizedException("Token subject does not match user record");
    }

    const profile = mapPrismaUserToDomain(prismaUser);

    return profile;
  }
}
