import {
  Body,
  Controller,
  Delete,
  Get,
  Param,
  Post,
  Req,
  Res,
  StreamableFile,
  UploadedFile,
  UseGuards,
  UseInterceptors,
} from "@nestjs/common";
import { FileInterceptor } from "@nestjs/platform-express";
import { Request, Response } from "express";
import { AuthenticatedGuard } from "../auth/authenticated.guard";
import { AbilityGuard } from "../auth/ability.guard";
import { RequireAbility } from "../auth/require-ability.decorator";
import { AuditService } from "../common/audit.service";
import { OpsService } from "./ops.service";
import { ReplicationService } from "./replication.service";
import { StartJobDto } from "./start-job.dto";

/** Every route is ADMIN-only (ability "db:manage"). */
@UseGuards(AuthenticatedGuard, AbilityGuard)
@RequireAbility("db:manage")
@Controller("ops")
export class OpsController {
  constructor(
    private readonly ops: OpsService,
    private readonly replication: ReplicationService,
    private readonly audit: AuditService,
  ) {}

  private actingId(req: Request): string {
    return (req.user as { id: string }).id;
  }

  /* ------------------------------------------------------------- ingest */

  @Get("ingest")
  listIngest() {
    return this.ops.listIngest();
  }

  @Post("ingest/:name")
  @UseInterceptors(
    FileInterceptor("file", { limits: { fileSize: 2 * 1024 * 1024 * 1024 } }),
  )
  async uploadIngest(
    @Param("name") name: string,
    @UploadedFile() file: { buffer: Buffer; size: number } | undefined,
    @Req() req: Request,
  ) {
    if (!file) throw new Error("No se recibió ningún archivo.");
    await this.ops.saveIngest(name, file.buffer);
    void this.audit.log(this.actingId(req), "ops.ingest.upload", {
      name,
      size: file.size,
    });
    return { ok: true };
  }

  @Delete("ingest/:name")
  async deleteIngest(@Param("name") name: string, @Req() req: Request) {
    await this.ops.deleteIngest(name);
    void this.audit.log(this.actingId(req), "ops.ingest.delete", { name });
    return { ok: true };
  }

  /* ------------------------------------------------------------ backups */

  @Get("backups")
  listBackups() {
    return this.ops.listBackups();
  }

  @Get("backups/:name/download")
  download(
    @Param("name") name: string,
    @Res({ passthrough: true }) res: Response,
  ): StreamableFile {
    const { stream, name: safe } = this.ops.backupStream(name);
    res.set({
      "Content-Type": "application/gzip",
      "Content-Disposition": `attachment; filename="${safe}"`,
    });
    return new StreamableFile(stream);
  }

  @Delete("backups/:name")
  async deleteBackup(@Param("name") name: string, @Req() req: Request) {
    await this.ops.deleteBackup(name);
    void this.audit.log(this.actingId(req), "ops.backup.delete", { name });
    return { ok: true };
  }

  /* --------------------------------------------------------------- jobs */

  /** Health of the my.jorgecuadros.com read replica. Read-only, no audit entry. */
  @Get("replication")
  replicationStatus() {
    return this.replication.status();
  }

  @Get("jobs")
  listJobs() {
    return this.ops.listJobs();
  }

  @Get("jobs/:id")
  getJob(@Param("id") id: string) {
    return this.ops.getJob(id);
  }

  @Post("jobs")
  async startJob(@Body() dto: StartJobDto, @Req() req: Request) {
    const userId = this.actingId(req);
    const job = await this.ops.startJob(dto.kind, { file: dto.file }, userId);
    void this.audit.log(userId, "ops.job.start", {
      jobId: job.id,
      kind: dto.kind,
      file: dto.file,
    });
    return job;
  }
}
