import {
  Body,
  Controller,
  Get,
  Post,
  Req,
  Res,
  UseGuards,
} from '@nestjs/common';
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
import type { Request, Response } from 'express';
import { ClinicOrgGuard } from '../../common/guards/clinic-org.guard';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { ExtractVoiceDto } from './dto/voice.dto';
import { VoiceThrottlerGuard } from './voice-throttler.guard';
import { VoiceService } from './voice.service';

type VoiceRequestUser = { id: string; organizationId?: string };

@ApiTags('voice')
@ApiBearerAuth('JWT-auth')
@UseGuards(JwtAuthGuard, ClinicOrgGuard)
@Controller('voice')
export class VoiceController {
  constructor(private readonly voiceService: VoiceService) {}

  @Get('availability')
  @ApiOperation({
    summary: 'Whether voice entry is configured, and for which locales',
  })
  getAvailability() {
    // The frontend cannot learn this from NEXT_PUBLIC_* — those are baked in at build
    // time, so enabling a locale would otherwise require rebuilding the image.
    return { success: true, data: this.voiceService.getAvailability() };
  }

  @Post('extract')
  // Guarded here rather than globally, and configured by VOICE_THROTTLE_* rather than
  // hardcoded. Availability is deliberately left unthrottled — it is cheap and the
  // frontend calls it on load.
  @UseGuards(VoiceThrottlerGuard)
  @ApiOperation({
    summary:
      'Transcribe a recording and extract treatment detail intents (TAB_TREATMENT_EDIT)',
  })
  async extract(
    @Req() req: Request & { user: VoiceRequestUser },
    @Res({ passthrough: true }) res: Response,
    @Body() dto: ExtractVoiceDto,
  ) {
    // Cancelling in the browser closes the connection; propagate it as an abort so the vendor
    // call stops rather than settling unseen. It is metered per minute.
    const aborter = new AbortController();
    res.on('close', () => {
      if (!res.writableFinished) aborter.abort();
    });

    // dto.locale, not req.user.language: the client sends the locale the microphone was
    // actually offered in, so the ASR hint, catalog labels and week start all match it.
    const data = await this.voiceService.extract(
      req.user,
      dto,
      dto.locale,
      aborter.signal,
    );
    return { success: true, data };
  }
}
