import { Body, Controller, Headers, Logger, Post, Req, type RawBodyRequest } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { createHmac, timingSafeEqual } from "node:crypto";
import type { Request } from "express";
import { SkipCsrf } from "../common/decorators/skip-csrf.decorator";
import { GatekeeperService } from "../gatekeeper/gatekeeper.service";
import { QueueNotificationsService } from "./queue-notifications.service";

export interface WoodpeckerWebhookPayload {
  branch: string;
  status: "success" | "failure";
  buildUrl: string;
  repo: string;
  prNumber?: number;
  headSha?: string;
}

@Controller("webhooks")
export class WoodpeckerWebhookController {
  private readonly logger = new Logger(WoodpeckerWebhookController.name);

  constructor(
    private readonly queueService: QueueNotificationsService,
    private readonly gatekeeperService: GatekeeperService,
    private readonly configService: ConfigService
  ) {}

  @SkipCsrf()
  @Post("woodpecker")
  async handleWebhook(
    @Req() req: RawBodyRequest<Request>,
    @Body() body: WoodpeckerWebhookPayload,
    @Headers("x-woodpecker-signature") signature: string
  ): Promise<{ ok: boolean; notified: number }> {
    const secret = this.configService.get<string>("WOODPECKER_WEBHOOK_SECRET");

    if (!secret) {
      this.logger.warn("WOODPECKER_WEBHOOK_SECRET is not configured; accepting Woodpecker webhook");
      const result = await this.queueService.notifyAgentCiResult(body);
      await this.forwardCiStatusToGatekeeper(body);
      return { ok: true, notified: result.notified };
    }

    if (!this.isValidSignature(this.getRequestBody(req, body), signature, secret)) {
      this.logger.warn("Received invalid Woodpecker webhook signature");
      return { ok: true, notified: 0 };
    }

    const result = await this.queueService.notifyAgentCiResult(body);
    await this.forwardCiStatusToGatekeeper(body);
    return { ok: true, notified: result.notified };
  }

  private async forwardCiStatusToGatekeeper(body: WoodpeckerWebhookPayload): Promise<void> {
    if (typeof body.prNumber === "number" && typeof body.headSha === "string") {
      await this.gatekeeperService.handleCiEvent(
        body.repo,
        body.prNumber,
        body.headSha,
        body.status
      );
    }
  }

  private getRequestBody(req: RawBodyRequest<Request>, body: WoodpeckerWebhookPayload): Buffer {
    const rawBody = req.rawBody;

    if (Buffer.isBuffer(rawBody)) {
      return rawBody;
    }

    return Buffer.from(JSON.stringify(body));
  }

  private isValidSignature(body: Buffer, signature: string | undefined, secret: string): boolean {
    if (!signature) {
      return false;
    }

    const expected = createHmac("sha256", secret).update(body).digest("hex");
    const signatureBuffer = Buffer.from(signature);
    const expectedBuffer = Buffer.from(expected);

    if (signatureBuffer.length !== expectedBuffer.length) {
      return false;
    }

    return timingSafeEqual(signatureBuffer, expectedBuffer);
  }
}
