import {
  CanActivate,
  ExecutionContext,
  Injectable,
  Logger,
  UnauthorizedException,
} from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { timingSafeEqual } from "node:crypto";
import { Request } from "express";

/**
 * Shared-secret service-to-service auth for the internal surface (/payments/*).
 * Callers send `x-service-token: <SERVICE_AUTH_TOKEN>` (or `Authorization: Bearer …`).
 * Webhook endpoints are intentionally NOT behind this guard — they are provider-facing and
 * authenticate via signature verification instead.
 */
@Injectable()
export class ServiceAuthGuard implements CanActivate {
  private readonly logger = new Logger(ServiceAuthGuard.name);
  private readonly token: string;
  private warned = false;

  constructor(config: ConfigService) {
    this.token = config.get<string>("app.serviceAuthToken") ?? "";
    if (!this.token && process.env.NODE_ENV === "production") {
      throw new Error("SERVICE_AUTH_TOKEN must be set in production");
    }
  }

  canActivate(context: ExecutionContext): boolean {
    if (!this.token) {
      if (!this.warned) {
        this.logger.warn(
          "SERVICE_AUTH_TOKEN unset — internal endpoints are UNGUARDED (dev only)",
        );
        this.warned = true;
      }
      return true;
    }

    const request = context.switchToHttp().getRequest<Request>();
    const header = request.headers["x-service-token"];
    const bearer = request.headers.authorization?.replace(/^Bearer\s+/i, "");
    const presented =
      (Array.isArray(header) ? header[0] : header) ?? bearer ?? "";

    const expected = Buffer.from(this.token);
    const actual = Buffer.from(presented);
    const valid =
      expected.length === actual.length && timingSafeEqual(expected, actual);
    if (!valid) throw new UnauthorizedException("Invalid service token");
    return true;
  }
}
