import {
  Body,
  Controller,
  Get,
  Header,
  Param,
  Post,
  Query,
  Res,
  UseGuards,
} from "@nestjs/common";
import type { Response } from "express";
import { AuthenticatedGuard } from "../auth/authenticated.guard";
import { ReportsService } from "./reports.service";
import {
  renderCsv,
  renderPdf,
  renderPrintHtml,
  renderXlsx,
} from "./outputs";
import { findReport } from "./reports.registry";

/**
 * Reports routes. Every report is dispatched by slug; outputs are
 * differentiated by `?format=...` (default `json`). Reads only — gated
 * by AuthenticatedGuard alone, like every other read in the app.
 */
@UseGuards(AuthenticatedGuard)
@Controller("reports")
export class ReportsController {
  constructor(private readonly reports: ReportsService) {}

  /** Catalog of all registered reports (the /reportes index). */
  @Get()
  catalog() {
    return { items: this.reports.catalog() };
  }

  /** Run a report and return the JSON result (rows + totals + the def's columns). */
  @Get(":slug")
  async runJson(
    @Param("slug") slug: string,
    @Query() query: Record<string, string | undefined>,
  ) {
    const def = findReport(slug);
    const result = await this.reports.run(slug, query);
    return { ...result, columns: def?.columns ?? [] };
  }

  /** CSV download. */
  @Get(":slug/csv")
  @Header("Content-Type", "text/csv; charset=utf-8")
  async runCsv(
    @Param("slug") slug: string,
    @Query() query: Record<string, string | undefined>,
    @Res() res: Response,
  ) {
    const def = findReport(slug);
    const result = await this.reports.run(slug, query);
    const filename = `${def?.title ?? slug}-${new Date().toISOString().slice(0, 10)}.csv`;
    res.setHeader(
      "Content-Disposition",
      `attachment; filename="${filename.replace(/[^\wÀ-ſ .-]/g, "_")}"`,
    );
    res.send(renderCsv(def?.columns ?? [], result));
  }

  /** XLSX download. */
  @Get(":slug/xlsx")
  async runXlsx(
    @Param("slug") slug: string,
    @Query() query: Record<string, string | undefined>,
    @Res() res: Response,
  ) {
    const def = findReport(slug);
    const result = await this.reports.run(slug, query);
    const filename = `${def?.title ?? slug}-${new Date().toISOString().slice(0, 10)}.xlsx`;
    const buf = await renderXlsx(def?.columns ?? [], result);
    res.setHeader(
      "Content-Type",
      "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
    );
    res.setHeader(
      "Content-Disposition",
      `attachment; filename="${filename.replace(/[^\wÀ-ſ .-]/g, "_")}"`,
    );
    res.send(buf);
  }

  /** PDF download. */
  @Get(":slug/pdf")
  async runPdf(
    @Param("slug") slug: string,
    @Query() query: Record<string, string | undefined>,
    @Res() res: Response,
  ) {
    const def = findReport(slug);
    const result = await this.reports.run(slug, query);
    const buf = await renderPdf(def?.columns ?? [], result, def?.title ?? slug);
    const filename = `${def?.title ?? slug}-${new Date().toISOString().slice(0, 10)}.pdf`;
    res.setHeader("Content-Type", "application/pdf");
    res.setHeader(
      "Content-Disposition",
      `attachment; filename="${filename.replace(/[^\wÀ-ſ .-]/g, "_")}"`,
    );
    res.send(buf);
  }

  /** Browser-printable HTML view (the user hits Print → Save as PDF). */
  @Get(":slug/print")
  @Header("Content-Type", "text/html; charset=utf-8")
  async runPrint(
    @Param("slug") slug: string,
    @Query() query: Record<string, string | undefined>,
  ) {
    const def = findReport(slug);
    const result = await this.reports.run(slug, query);
    return renderPrintHtml(def?.columns ?? [], result, def?.title ?? slug);
  }

  /** POST a customer-picker-driven report (statement). Mirrors GET to keep
   *  the param contract simple: same body shape, same response. */
  @Post(":slug")
  async runPost(
    @Param("slug") slug: string,
    @Body() body: Record<string, string | undefined>,
  ) {
    return this.reports.run(slug, body);
  }
}
