import { CurrentUser } from "@edr/api-common";
import { SUPPORT_ATTACHMENT_RESOURCE } from "@edr/types";
import {
  Controller,
  ForbiddenException,
  Get,
  NotFoundException,
  Param,
  ParseUUIDPipe,
  Query,
  Res,
} from "@nestjs/common";
import {
  ApiBearerAuth,
  ApiOperation,
  ApiQuery,
  ApiTags,
} from "@nestjs/swagger";
import { Response } from "express";

import {
  AuthUserPayload,
  resolveAuthUserId,
} from "../../common/resolve-auth-user-id";
import { FilesService } from "../files/files.service";
import { SupportChatService } from "./support-chat.service";

/**
 * Authenticated download for chat attachments.
 *
 * This exists instead of reusing `GET /files/:fileId` because that route streams
 * any file to any authenticated caller who knows its UUID — fine-ish for a
 * booking document the caller already had a link to, not fine for chat, where
 * one customer guessing another's file id would be a cross-tenant leak. That
 * route now refuses `support_message` files outright and points here.
 *
 * Inline previews still use the short-lived signed URL on the message DTO — a
 * browser `<img>` can't send a Bearer token. This route is for explicit
 * downloads and for clients that would rather stream through the API.
 */
@ApiTags("support-chat")
@ApiBearerAuth()
@Controller("support/attachments")
export class SupportAttachmentController {
  constructor(
    private readonly files: FilesService,
    private readonly chat: SupportChatService,
  ) {}

  @Get(":fileId")
  @ApiOperation({
    summary: "Download a support chat attachment",
    description:
      "Streams the file only if the caller is backoffice staff or belongs to the " +
      "company that owns the thread the attachment was posted in.",
  })
  @ApiQuery({
    name: "download",
    required: false,
    description: "Set to 1/true to force a download instead of inline preview.",
  })
  async download(
    @CurrentUser() user: AuthUserPayload,
    @Param("fileId", ParseUUIDPipe) fileId: string,
    @Query("download") download: string | undefined,
    @Res() res: Response,
  ) {
    const record = await this.files.findById(fileId);

    // Don't let this route become a second general-purpose file endpoint: it can
    // only vouch for chat attachments, so anything else is a 404 (not a 403 —
    // no reason to confirm the id exists).
    if (record.resource !== SUPPORT_ATTACHMENT_RESOURCE) {
      throw new NotFoundException(`File ${fileId} not found`);
    }

    const allowed = await this.chat.canUserAccessMessage(
      record.resourceId,
      resolveAuthUserId(user),
    );
    if (!allowed) {
      throw new ForbiddenException(
        "This attachment belongs to another company's conversation.",
      );
    }

    const { stream } = await this.files.streamById(fileId);
    const forceDownload = download === "1" || download === "true";

    res.setHeader("Content-Type", record.mimeType);
    res.setHeader(
      "Content-Disposition",
      `${forceDownload ? "attachment" : "inline"}; filename="${record.name}"`,
    );
    // Private only — this response is scoped to one caller's authorization, so a
    // shared cache must never reuse it for the next person asking.
    res.setHeader("Cache-Control", "private, max-age=300");
    stream.pipe(res);
  }
}
