import {
  Body,
  Controller,
  Delete,
  Get,
  Param,
  ParseIntPipe,
  Patch,
  Post,
  Put,
  Query,
  Req,
  Res,
  UploadedFiles,
  UseGuards,
  UseInterceptors,
} from '@nestjs/common';
import { FilesInterceptor } from '@nestjs/platform-express';
import { ApiBearerAuth, ApiBody, ApiConsumes, ApiOperation, ApiTags } from '@nestjs/swagger';
import { memoryStorage } from 'multer';
import type { Response } from 'express';
import { ClinicOrgGuard } from '../../common/guards/clinic-org.guard';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import {
  SaveTreatmentDraftDto,
  SaveTreatmentLabCasesDto,
  UpdateLabCaseDueDateDto,
  CreateStandaloneTreatmentDto,
  ListDayTreatmentsDto,
} from './dto/treatment.dto';
import { CreateLabCaseCommentDto } from '../lab-case-comments/dto/lab-case-comment.dto';
import { LabCaseCommentsService } from '../lab-case-comments/lab-case-comments.service';
import { TreatmentsService } from './treatments.service';

@ApiTags('treatments')
@ApiBearerAuth('JWT-auth')
@UseGuards(JwtAuthGuard, ClinicOrgGuard)
@Controller('treatments')
export class TreatmentsController {
  constructor(
    private readonly treatmentsService: TreatmentsService,
    private readonly commentsService: LabCaseCommentsService,
  ) {}

  @Get('linked-organizations')
  @ApiOperation({ summary: 'List active linked counterpart organizations (TAB_TREATMENT_READ)' })
  listLinkedOrganizations(@Req() req: { user: { id: string; organizationId?: string } }) {
    const organizationId = this.treatmentsService.getOrganizationIdFromUser(req.user);
    return this.treatmentsService.listLinkedOrganizations(req.user.id, organizationId);
  }

  @Get('lab-cases/unread')
  @ApiOperation({ summary: 'Sent lab cases with unread lab activity for the organization (TAB_TREATMENT_READ)' })
  listUnreadLabCases(@Req() req: { user: { id: string; organizationId?: string } }) {
    const organizationId = this.treatmentsService.getOrganizationIdFromUser(req.user);
    return this.treatmentsService.listUnreadLabCases(organizationId, req.user.id);
  }

  @Get('patients/:patientId/lab-cases')
  @ApiOperation({ summary: 'Sent lab cases for a patient with tracker summaries (TAB_TREATMENT_READ)' })
  listPatientLabCases(
    @Param('patientId') patientId: string,
    @Req() req: { user: { id: string; organizationId?: string } },
  ) {
    const organizationId = this.treatmentsService.getOrganizationIdFromUser(req.user);
    return this.treatmentsService.listPatientLabCases(
      patientId,
      organizationId,
      req.user.id,
    );
  }

  @Get('patients/:patientId/history')
  @ApiOperation({ summary: 'List treatments for a patient (draft and completed, TAB_TREATMENT_READ)' })
  listPatientHistory(
    @Param('patientId') patientId: string,
    @Query('limit', new ParseIntPipe({ optional: true })) limit = 20,
    @Req() req: { user: { id: string; organizationId?: string } },
  ) {
    const organizationId = this.treatmentsService.getOrganizationIdFromUser(req.user);
    return this.treatmentsService.listPatientHistory(
      patientId,
      organizationId,
      req.user.id,
      limit,
    );
  }

  @Get('day')
  @ApiOperation({ summary: 'Standalone (no appointment) treatments for a local day range' })
  listDayStandalone(
    @Query() query: ListDayTreatmentsDto,
    @Req() req: { user: { id: string; organizationId?: string } },
  ) {
    const organizationId = this.treatmentsService.getOrganizationIdFromUser(req.user);
    return this.treatmentsService.listDayStandalone(
      query.from,
      query.to,
      organizationId,
      req.user.id,
    );
  }

  @Post()
  @ApiOperation({ summary: 'Create a treatment without an appointment (TAB_TREATMENT_EDIT)' })
  createStandalone(
    @Body() dto: CreateStandaloneTreatmentDto,
    @Req() req: { user: { id: string; organizationId?: string } },
  ) {
    const organizationId = this.treatmentsService.getOrganizationIdFromUser(req.user);
    return this.treatmentsService.createStandalone(dto, organizationId, req.user.id);
  }

  @Get('appointments/:appointmentId/draft')
  @ApiOperation({ summary: 'Get draft treatment for an appointment (TAB_TREATMENT_READ)' })
  getDraft(
    @Param('appointmentId') appointmentId: string,
    @Req() req: { user: { id: string; organizationId?: string; language?: string | null } },
  ) {
    const organizationId = this.treatmentsService.getOrganizationIdFromUser(req.user);
    return this.treatmentsService.getDraftForAppointment(
      appointmentId,
      organizationId,
      req.user.id,
      req.user.language,
    );
  }

  @Put('appointments/:appointmentId/draft')
  @ApiOperation({ summary: 'Save draft treatment details for an appointment (TAB_TREATMENT_EDIT)' })
  saveDraft(
    @Param('appointmentId') appointmentId: string,
    @Body() dto: SaveTreatmentDraftDto,
    @Req() req: { user: { id: string; organizationId?: string; language?: string | null } },
  ) {
    const organizationId = this.treatmentsService.getOrganizationIdFromUser(req.user);
    return this.treatmentsService.saveDraftForAppointment(
      appointmentId,
      dto,
      organizationId,
      req.user.id,
      req.user.language,
    );
  }

  @Put('appointments/:appointmentId/lab-cases')
  @ApiOperation({ summary: 'Save lab case groupings for a draft treatment (TAB_TREATMENT_EDIT)' })
  saveLabCases(
    @Param('appointmentId') appointmentId: string,
    @Body() dto: SaveTreatmentLabCasesDto,
    @Req() req: { user: { id: string; organizationId?: string; language?: string | null } },
  ) {
    const organizationId = this.treatmentsService.getOrganizationIdFromUser(req.user);
    return this.treatmentsService.saveLabCasesForAppointment(
      appointmentId,
      dto,
      organizationId,
      req.user.id,
      req.user.language,
    );
  }

  @Post('appointments/:appointmentId/details/:detailClientKey/attachments')
  @ApiOperation({ summary: 'Upload attachments for a draft treatment detail (TAB_TREATMENT_EDIT)' })
  @ApiConsumes('multipart/form-data')
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        files: {
          type: 'array',
          items: { type: 'string', format: 'binary' },
        },
      },
    },
  })
  @UseInterceptors(
    FilesInterceptor('files', 20, {
      storage: memoryStorage(),
    }),
  )
  uploadDetailAttachments(
    @Param('appointmentId') appointmentId: string,
    @Param('detailClientKey') detailClientKey: string,
    @UploadedFiles() files: Express.Multer.File[],
    @Req() req: { user: { id: string; organizationId?: string } },
  ) {
    const organizationId = this.treatmentsService.getOrganizationIdFromUser(req.user);
    return this.treatmentsService.uploadDetailAttachments(
      appointmentId,
      detailClientKey,
      files,
      organizationId,
      req.user.id,
    );
  }

  /** @deprecated Use details/:detailClientKey/attachments */
  @Post('appointments/:appointmentId/cases/:caseClientKey/attachments')
  @ApiOperation({ summary: 'Legacy alias for detail attachment upload' })
  @ApiConsumes('multipart/form-data')
  @UseInterceptors(
    FilesInterceptor('files', 20, {
      storage: memoryStorage(),
    }),
  )
  uploadDetailAttachmentsLegacy(
    @Param('appointmentId') appointmentId: string,
    @Param('caseClientKey') caseClientKey: string,
    @UploadedFiles() files: Express.Multer.File[],
    @Req() req: { user: { id: string; organizationId?: string } },
  ) {
    const organizationId = this.treatmentsService.getOrganizationIdFromUser(req.user);
    return this.treatmentsService.uploadDetailAttachments(
      appointmentId,
      caseClientKey,
      files,
      organizationId,
      req.user.id,
    );
  }

  @Get('attachments/:attachmentId/file')
  @ApiOperation({ summary: 'Download a treatment attachment (TAB_TREATMENT_READ)' })
  async downloadAttachment(
    @Param('attachmentId') attachmentId: string,
    @Req() req: { user: { id: string; organizationId?: string } },
    @Res() res: Response,
  ) {
    const organizationId = this.treatmentsService.getOrganizationIdFromUser(req.user);
    const file = await this.treatmentsService.streamAttachmentFile(
      attachmentId,
      organizationId,
      req.user.id,
    );

    res.setHeader('Content-Type', file.mimeType);
    res.setHeader('Content-Disposition', `inline; filename="${file.fileName}"`);
    file.stream.pipe(res);
  }

  @Post('lab-cases/:labCaseId/send')
  @ApiOperation({ summary: 'Send a lab case to its destination organization (TAB_TREATMENT_EDIT)' })
  sendLabCase(
    @Param('labCaseId') labCaseId: string,
    @Req() req: { user: { id: string; organizationId?: string; language?: string | null } },
  ) {
    const organizationId = this.treatmentsService.getOrganizationIdFromUser(req.user);
    return this.treatmentsService.sendLabCase(
      labCaseId,
      organizationId,
      req.user.id,
      req.user.language,
    );
  }

  @Patch('lab-cases/:labCaseId/due-date')
  @ApiOperation({ summary: 'Update expected due date for a sent lab case' })
  updateLabCaseDueDate(
    @Param('labCaseId') labCaseId: string,
    @Body() dto: UpdateLabCaseDueDateDto,
    @Req() req: { user: { id: string; organizationId?: string } },
  ) {
    const organizationId = this.treatmentsService.getOrganizationIdFromUser(req.user);
    return this.treatmentsService.updateLabCaseDueDate(
      labCaseId,
      dto,
      organizationId,
      req.user.id,
    );
  }

  @Get('lab-cases/:labCaseId/comments')
  @ApiOperation({ summary: 'List comments for a lab case during treatment dispatch' })
  listLabCaseComments(
    @Param('labCaseId') labCaseId: string,
    @Req() req: { user: { id: string; organizationId?: string } },
  ) {
    const organizationId = this.treatmentsService.getOrganizationIdFromUser(req.user);
    return this.commentsService.listForClinicTreatmentCase(
      labCaseId,
      organizationId,
      req.user.id,
    );
  }

  @Post('lab-cases/:labCaseId/comments')
  @ApiOperation({ summary: 'Add a comment to a lab case during treatment dispatch' })
  addLabCaseComment(
    @Param('labCaseId') labCaseId: string,
    @Body() dto: CreateLabCaseCommentDto,
    @Req() req: { user: { id: string; organizationId?: string } },
  ) {
    const organizationId = this.treatmentsService.getOrganizationIdFromUser(req.user);
    return this.commentsService.addForClinicTreatmentCase(
      labCaseId,
      organizationId,
      req.user.id,
      dto,
    );
  }

  @Get(':treatmentId/draft')
  @ApiOperation({ summary: 'Get a treatment draft by treatment id (TAB_TREATMENT_READ)' })
  getDraftByTreatment(
    @Param('treatmentId') treatmentId: string,
    @Req() req: { user: { id: string; organizationId?: string; language?: string | null } },
  ) {
    const organizationId = this.treatmentsService.getOrganizationIdFromUser(req.user);
    return this.treatmentsService.getDraftForTreatment(
      treatmentId,
      organizationId,
      req.user.id,
      req.user.language,
    );
  }

  @Put(':treatmentId/draft')
  @ApiOperation({ summary: 'Save draft treatment details by treatment id (TAB_TREATMENT_EDIT)' })
  saveDraftByTreatment(
    @Param('treatmentId') treatmentId: string,
    @Body() dto: SaveTreatmentDraftDto,
    @Req() req: { user: { id: string; organizationId?: string; language?: string | null } },
  ) {
    const organizationId = this.treatmentsService.getOrganizationIdFromUser(req.user);
    return this.treatmentsService.saveDraftForTreatment(
      treatmentId,
      dto,
      organizationId,
      req.user.id,
      req.user.language,
    );
  }

  @Put(':treatmentId/lab-cases')
  @ApiOperation({ summary: 'Save lab case groupings by treatment id (TAB_TREATMENT_EDIT)' })
  saveLabCasesByTreatment(
    @Param('treatmentId') treatmentId: string,
    @Body() dto: SaveTreatmentLabCasesDto,
    @Req() req: { user: { id: string; organizationId?: string; language?: string | null } },
  ) {
    const organizationId = this.treatmentsService.getOrganizationIdFromUser(req.user);
    return this.treatmentsService.saveLabCasesForTreatment(
      treatmentId,
      dto,
      organizationId,
      req.user.id,
      req.user.language,
    );
  }

  @Post(':treatmentId/details/:detailClientKey/attachments')
  @ApiOperation({ summary: 'Upload attachments for a standalone treatment detail' })
  @ApiConsumes('multipart/form-data')
  @UseInterceptors(
    FilesInterceptor('files', 20, {
      storage: memoryStorage(),
    }),
  )
  uploadDetailAttachmentsByTreatment(
    @Param('treatmentId') treatmentId: string,
    @Param('detailClientKey') detailClientKey: string,
    @UploadedFiles() files: Express.Multer.File[],
    @Req() req: { user: { id: string; organizationId?: string } },
  ) {
    const organizationId = this.treatmentsService.getOrganizationIdFromUser(req.user);
    return this.treatmentsService.uploadDetailAttachmentsForTreatment(
      treatmentId,
      detailClientKey,
      files,
      organizationId,
      req.user.id,
    );
  }

  @Delete(':treatmentId')
  @ApiOperation({ summary: 'Delete an empty standalone treatment (no details)' })
  deleteStandalone(
    @Param('treatmentId') treatmentId: string,
    @Req() req: { user: { id: string; organizationId?: string } },
  ) {
    const organizationId = this.treatmentsService.getOrganizationIdFromUser(req.user);
    return this.treatmentsService.deleteStandalone(treatmentId, organizationId, req.user.id);
  }
}
