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

/**
 * Shared-secret guard for endpoints only the payment microservice may call
 * (e.g. /internal/payments/mark-paid). The secret is the same SERVICE_AUTH_TOKEN
 * the payment service uses on its own internal surface.
 */
@Injectable()
export class ServiceAuthGuard implements CanActivate {
    private readonly logger = new Logger(ServiceAuthGuard.name);
    private readonly token = process.env.SERVICE_AUTH_TOKEN ?? "";
    private warned = false;

    constructor() {
        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;
    }
}
