// src/clients/clients.controller.ts
import {
  BadRequestException,
  Controller,
  Get,
  Logger,
  NotFoundException,
  Param,
  ParseIntPipe,
  Query,
  Req,
  Res,
  StreamableFile,
} from '@nestjs/common';
import type { Request, Response } from 'express';
import { ClientsService } from './clients.service';
import { ClientSignedDocService } from './client-signed-doc.service';
import { ListClientsDto } from './dto/list-clients.dto';

@Controller('/clients')
export class ClientsController {
  private readonly logger = new Logger('ClientsController');

  constructor(
    private readonly clientsService: ClientsService,
    private readonly signedDocService: ClientSignedDocService,
  ) {}

  /** Same helper the previous-nc controller uses. */
  private getCurrentUserId(req: Request): number {
    const user = (req as any).user;
    const id = user?.id ?? user?.userId ?? user?.sub;
    if (id === undefined || id === null) {
      throw new BadRequestException(
        'User id not found on request.user (JwtAuthGuard / strategy issue?)',
      );
    }
    return Number(id);
  }

  /** Same source validator the audit-report controller uses. */
  private validSource(s: string): 'QRS' | 'TQS' {
    const up = (s || '').toUpperCase();
    if (up !== 'QRS' && up !== 'TQS') {
      throw new BadRequestException('source must be QRS or TQS');
    }
    return up as 'QRS' | 'TQS';
  }

  // GET /clients/paged-clients
  @Get('/paged-clients')
  async getPagedClients(@Query() q: ListClientsDto, @Req() req: Request) {
    const userId = this.getCurrentUserId(req);
    return this.clientsService.getClientsPagedFiltered(q, userId);
  }

  // GET /clients/paged-surves
  @Get('/paged-surves')
  async getPagedSurves(@Query() q: ListClientsDto, @Req() req: Request) {
    const userId = this.getCurrentUserId(req);
    return this.clientsService.getSurvesPagedFiltered(q, userId);
  }

  // GET /clients/paged-all
  @Get('/paged-all')
  async getAllPaged(@Query() q: ListClientsDto, @Req() req: Request) {
    const userId = this.getCurrentUserId(req);
    return this.clientsService.getAllCombinedPaged(q, userId);
  }

  // GET /clients/my-scope  → debug the id mapping
  @Get('/my-scope')
  async myScope(@Req() req: Request) {
    return this.clientsService.describeScope(this.getCurrentUserId(req));
  }
  // GET /clients/search-exact?name=al geemi&source=QRS
  // 🔒 Partial, case-insensitive client search, scoped to the
  // logged-in user (same ownership logic as previous-nc). Client
  // rows only — Surveillance never appears. Frontend renders the
  // best row per company selectable and duplicates disabled.
  @Get('/search-exact')
  async searchExact(
    @Query('name') name: string,
    @Query('source') source: string,
    @Req() req: Request,
  ) {
    const userId = this.getCurrentUserId(req);
    return this.clientsService.searchExact(
      name ?? '',
      this.validSource(source || 'QRS'),
      userId,
    );
  }
  // ═══════════════════════════════════════════════════════════════
  // SIGNED DOCUMENT — same two strategies as audit-report file/url
  // and file/stream. Keep these ABOVE /details/:id (specific routes
  // before ":param" routes — same rule as the audit-report controller).
  // ═══════════════════════════════════════════════════════════════

  // GET /clients/signed-doc/url?source=QRS&id=123
  // Returns { url, filename, docname } — the frontend just window.open(url)s it.
  @Get('/signed-doc/url')
  async getSignedDocUrl(
    @Query('source') source: string,
    @Query('id', ParseIntPipe) id: number,
    @Req() req: Request,
  ) {
    return this.signedDocService.getPublicUrl(
      this.validSource(source),
      id,
      this.getCurrentUserId(req),
    );
  }

  // GET /clients/signed-doc/stream?source=QRS&id=123
  // Streams the actual file bytes from disk (Strategy B).
  @Get('/signed-doc/stream')
  async streamSignedDoc(
    @Query('source') source: string,
    @Query('id', ParseIntPipe) id: number,
    @Req() req: Request,
    @Res({ passthrough: true }) res: Response,
  ): Promise<StreamableFile> {
    const { stream, size, mimeType, filename } =
      await this.signedDocService.openFile(
        this.validSource(source),
        id,
        this.getCurrentUserId(req),
      );

    res.set({
      'Content-Type': mimeType,
      'Content-Length': size.toString(),
      'Content-Disposition': `inline; filename="${encodeURIComponent(filename)}"`,
      'Cache-Control': 'private, max-age=300',
    });
    return new StreamableFile(stream);
  }

  // GET /clients/details/:id?source=QRS&type=Client
  @Get('/details/:id')
  async getClientDetails(
    @Param('id', ParseIntPipe) id: number,
    @Query('source') source: 'QRS' | 'TQS' = 'QRS',
    @Query('type') type: 'Client' | 'Surveillance' = 'Client',
    @Req() req: Request,
  ) {
    const userId = this.getCurrentUserId(req);
    const details = await this.clientsService.getClientDetails(
      id,
      source,
      type,
      userId,
    );
    if (!details) {
      throw new NotFoundException(`Client ${id} not found in ${source} ${type}`);
    }
    return details;
  }
}