import {
  Body,
  Controller,
  Delete,
  Get,
  Param,
  Patch,
  Post,
  Put,
  Query,
  Req,
  Res,
  UploadedFiles,
  UseGuards,
  UseInterceptors,
} from '@nestjs/common';
import { FilesInterceptor } from '@nestjs/platform-express';
import { memoryStorage } from 'multer';
import type { Response } from 'express';
import { ApiBearerAuth, ApiConsumes, ApiOperation, ApiTags } from '@nestjs/swagger';
import { LabOrgGuard } from '../../common/guards/lab-org.guard';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { CasesService } from './cases.service';
import {
  ListLabCasesDto,
  UpdateLabCaseImportantDto,
  UpdateLabCaseExternalCodeDto,
  AssignLabCaseTaskDto,
  CreateLabInternalCaseDto,
  UpdateLabInternalCaseDto,
} from './dto/cases.dto';

@ApiTags('cases')
@ApiBearerAuth('JWT-auth')
@UseGuards(JwtAuthGuard, LabOrgGuard)
@Controller('cases')
export class CasesController {
  constructor(private readonly casesService: CasesService) {}

  @Get()
  @ApiOperation({ summary: 'List lab cases received by this organization' })
  list(@Query() query: ListLabCasesDto, @Req() req) {
    const organizationId = this.casesService.getOrganizationIdFromUser(req.user);
    return this.casesService.list(organizationId, req.user.id, query);
  }

  @Post()
  @ApiOperation({ summary: 'Create a lab-origin draft case (TAB_CASES_EDIT)' })
  create(@Body() dto: CreateLabInternalCaseDto, @Req() req) {
    const organizationId = this.casesService.getOrganizationIdFromUser(req.user);
    return this.casesService.createInternal(dto, organizationId, req.user.id, req.user.language);
  }

  @Get('assignable-staff')
  @ApiOperation({ summary: 'Staff who can be assigned lab tasks' })
  listAssignableStaff(@Req() req) {
    const organizationId = this.casesService.getOrganizationIdFromUser(req.user);
    return this.casesService.listAssignableStaff(organizationId, req.user.id);
  }

  @Get('filter-options')
  @ApiOperation({ summary: 'Clinics and treatment types for inbox filters' })
  listFilterOptions(@Req() req) {
    const organizationId = this.casesService.getOrganizationIdFromUser(req.user);
    return this.casesService.listFilterOptions(organizationId, req.user.id);
  }

  @Get('linked-clinics')
  @ApiOperation({ summary: 'Active linked clinic organizations for lab-origin cases' })
  listLinkedClinics(@Req() req) {
    const organizationId = this.casesService.getOrganizationIdFromUser(req.user);
    return this.casesService.listLinkedClinics(organizationId, req.user.id);
  }

  @Get(':id')
  @ApiOperation({ summary: 'Get one lab case with tasks grouped by tooth' })
  getOne(@Param('id') id: string, @Req() req) {
    const organizationId = this.casesService.getOrganizationIdFromUser(req.user);
    return this.casesService.getOne(id, organizationId, req.user.id, req.user.language);
  }

  @Put(':id')
  @ApiOperation({ summary: 'Update a lab-origin draft case and its lines' })
  update(@Param('id') id: string, @Body() dto: UpdateLabInternalCaseDto, @Req() req) {
    const organizationId = this.casesService.getOrganizationIdFromUser(req.user);
    return this.casesService.updateInternal(id, dto, organizationId, req.user.id, req.user.language);
  }

  @Post(':id/start')
  @ApiOperation({ summary: 'Start a lab-origin draft (generate tasks, no clinic notify)' })
  start(@Param('id') id: string, @Req() req) {
    const organizationId = this.casesService.getOrganizationIdFromUser(req.user);
    return this.casesService.startInternal(id, organizationId, req.user.id, req.user.language);
  }

  @Delete(':id')
  @ApiOperation({ summary: 'Delete an unstarted lab-origin draft (TAB_CASES_EDIT)' })
  deleteDraft(@Param('id') id: string, @Req() req) {
    const organizationId = this.casesService.getOrganizationIdFromUser(req.user);
    return this.casesService.deleteInternalDraft(id, organizationId, req.user.id);
  }

  @Post(':id/lines/:lineClientKey/attachments')
  @ApiOperation({ summary: 'Upload attachments for a lab-origin draft line' })
  @ApiConsumes('multipart/form-data')
  @UseInterceptors(
    FilesInterceptor('files', 20, {
      storage: memoryStorage(),
    }),
  )
  uploadAttachments(
    @Param('id') id: string,
    @Param('lineClientKey') lineClientKey: string,
    @UploadedFiles() files: Express.Multer.File[],
    @Req() req,
  ) {
    const organizationId = this.casesService.getOrganizationIdFromUser(req.user);
    return this.casesService.uploadInternalAttachments(
      id,
      lineClientKey,
      files,
      organizationId,
      req.user.id,
    );
  }

  @Get(':id/attachments/:attachmentId/file')
  @ApiOperation({ summary: 'Download an attachment shared with this lab case' })
  async downloadAttachment(
    @Param('id') id: string,
    @Param('attachmentId') attachmentId: string,
    @Req() req,
    @Res() res: Response,
  ) {
    const organizationId = this.casesService.getOrganizationIdFromUser(req.user);
    const file = await this.casesService.streamCaseAttachment(
      id,
      attachmentId,
      organizationId,
      req.user.id,
    );
    res.setHeader('Content-Type', file.mimeType);
    res.setHeader('Content-Disposition', `inline; filename="${file.fileName}"`);
    file.stream.pipe(res);
  }

  @Patch(':id/important')
  @ApiOperation({ summary: 'Toggle the important flag for a whole case' })
  setCaseImportant(
    @Param('id') id: string,
    @Body() dto: UpdateLabCaseImportantDto,
    @Req() req,
  ) {
    const organizationId = this.casesService.getOrganizationIdFromUser(req.user);
    return this.casesService.setCaseImportant(id, dto, organizationId, req.user.id, req.user.language);
  }

  @Patch(':id/external-code')
  @ApiOperation({ summary: 'Set or clear the optional external order code for a case' })
  setCaseExternalCode(
    @Param('id') id: string,
    @Body() dto: UpdateLabCaseExternalCodeDto,
    @Req() req,
  ) {
    const organizationId = this.casesService.getOrganizationIdFromUser(req.user);
    return this.casesService.setCaseExternalCode(id, dto, organizationId, req.user.id, req.user.language);
  }

  @Patch(':caseId/tasks/:taskId/assign')
  @ApiOperation({ summary: 'Assign or unassign a task to lab staff' })
  assignTask(
    @Param('caseId') caseId: string,
    @Param('taskId') taskId: string,
    @Body() dto: AssignLabCaseTaskDto,
    @Req() req,
  ) {
    const organizationId = this.casesService.getOrganizationIdFromUser(req.user);
    return this.casesService.assignTask(
      caseId,
      taskId,
      dto,
      organizationId,
      req.user.id,
      req.user.language,
    );
  }
}
