import { Controller, HttpException, InternalServerErrorException, Param, Post, Patch, Body, UseGuards, Get, UseInterceptors, UploadedFile, } from "@nestjs/common"; import { ApiBearerAuth, ApiParam, ApiTags, ApiOperation, ApiResponse, ApiBody, ApiConsumes } from "@nestjs/swagger"; import { FileInterceptor } from "@nestjs/platform-express"; import { diskStorage } from "multer"; import { extname } from "path"; import { GlobalGuard } from "src/auth/guards/global.guard"; import { RolesGuard } from "src/auth/guards/role.guard"; import { Roles } from "src/decorators/roles.decorator"; import { CurrentUser } from "src/decorators/user.decorator"; import { RoleEnum } from "src/Types&Enums/role.enum"; import { ClaimRequestManagementService } from "./claim-request-management.service"; import { SelectOuterPartsV2Dto, SelectOuterPartsV2ResponseDto } from "./dto/select-outer-parts-v2.dto"; import { SelectOtherPartsV2Dto, SelectOtherPartsV2ResponseDto } from "./dto/select-other-parts-v2.dto"; import { GetCaptureRequirementsV2ResponseDto } from "./dto/capture-requirements-v2.dto"; import { UploadRequiredDocumentV2Dto, UploadRequiredDocumentV2ResponseDto } from "./dto/upload-document-v2.dto"; import { CapturePartV2Dto, CapturePartV2ResponseDto } from "./dto/capture-part-v2.dto"; import { GetMyClaimsV2ResponseDto } from "./dto/my-claims-v2.dto"; import { ClaimDetailsV2ResponseDto } from "./dto/claim-details-v2.dto"; @ApiTags("claim-request-management (v2)") @Controller("v2/claim-request-management") @ApiBearerAuth() @UseGuards(GlobalGuard, RolesGuard) @Roles(RoleEnum.USER) export class ClaimRequestManagementV2Controller { constructor( private readonly claimRequestManagementService: ClaimRequestManagementService, ) {} @Get("requests") @ApiOperation({ summary: "Get My Claims (V2)", description: "Get list of all claim requests for the current user.", }) @ApiResponse({ status: 200, description: "List of user claims", type: GetMyClaimsV2ResponseDto, }) async getMyClaims(@CurrentUser() user: any): Promise { try { return await this.claimRequestManagementService.getMyClaimsV2(user.sub); } catch (error) { if (error instanceof HttpException) throw error; throw new InternalServerErrorException( error instanceof Error ? error.message : "Failed to get claims list", ); } } @Get("request/:claimRequestId") @ApiParam({ name: "claimRequestId", description: "The claim case ID (MongoDB ObjectId)", example: "507f1f77bcf86cd799439011", }) @ApiOperation({ summary: "Get Claim Details (V2)", description: "Get full details of a claim request. Only the claim owner can access.", }) @ApiResponse({ status: 200, description: "Claim details", type: ClaimDetailsV2ResponseDto, }) @ApiResponse({ status: 403, description: "User is not the claim owner", }) @ApiResponse({ status: 404, description: "Claim not found", }) async getClaimDetails( @Param("claimRequestId") claimRequestId: string, @CurrentUser() user: any, ): Promise { try { return await this.claimRequestManagementService.getClaimDetailsV2( claimRequestId, user.sub, ); } catch (error) { if (error instanceof HttpException) throw error; throw new InternalServerErrorException( error instanceof Error ? error.message : "Failed to get claim details", ); } } @Post("create-from-blame/:blameRequestId") @ApiParam({ name: "blameRequestId", description: "ID of the completed blame case to create claim from", }) async createClaimFromBlame( @Param("blameRequestId") blameRequestId: string, @CurrentUser() user: any, ) { try { return await this.claimRequestManagementService.createClaimFromBlameV2( blameRequestId, user.sub, ); } catch (error) { if (error instanceof HttpException) throw error; throw new InternalServerErrorException( error instanceof Error ? error.message : "Failed to create claim request", ); } } /** * V2 API: Select damaged outer car parts (Step 2 of claim workflow) */ @Patch("select-outer-parts/:claimRequestId") @ApiOperation({ summary: "Select Damaged Outer Car Parts (V2 - Step 2)", description: ` **Workflow Step:** SELECT_OUTER_PARTS (Step 2 of Claim) **Purpose:** User selects which outer car parts (body parts) were damaged in the accident. **Validations:** - Claim must exist - User must be the claim owner (damaged party from blame case) - Current workflow step must be CLAIM_CREATED - Parts array must contain at least 1 part - No duplicate parts allowed - Parts cannot be re-selected once submitted **Valid Outer Parts:** - hood, trunk, roof - front_right_door, front_left_door, rear_right_door, rear_left_door - front_bumper, rear_bumper - front_right_fender, front_left_fender, rear_right_fender, rear_left_fender **After Success:** - Workflow moves to: SELECT_OTHER_PARTS (Step 3) - User can proceed to select non-body parts and provide bank info `, }) @ApiParam({ name: "claimRequestId", description: "The claim case ID (MongoDB ObjectId)", example: "507f1f77bcf86cd799439011", }) @ApiBody({ type: SelectOuterPartsV2Dto, description: "Array of selected damaged outer parts", examples: { example1: { summary: "Minor front damage", value: { selectedParts: ["hood", "front_bumper", "front_right_fender"], }, }, example2: { summary: "Side impact damage", value: { selectedParts: [ "front_left_door", "rear_left_door", "front_left_fender", "rear_left_fender", ], }, }, example3: { summary: "Rear-end collision", value: { selectedParts: ["rear_bumper", "trunk", "rear_right_fender"], }, }, example4: { summary: "Multiple damage areas", value: { selectedParts: [ "hood", "front_bumper", "front_right_door", "rear_bumper", "trunk", ], }, }, }, }) @ApiResponse({ status: 200, description: "Outer parts selected successfully", type: SelectOuterPartsV2ResponseDto, }) @ApiResponse({ status: 400, description: "Invalid workflow step or validation failed", }) @ApiResponse({ status: 403, description: "User is not the claim owner", }) @ApiResponse({ status: 404, description: "Claim case not found", }) @ApiResponse({ status: 409, description: "Outer parts already selected", }) async selectOuterParts( @Param("claimRequestId") claimRequestId: string, @Body() body: SelectOuterPartsV2Dto, @CurrentUser() user: any, ): Promise { try { return await this.claimRequestManagementService.selectOuterPartsV2( claimRequestId, body, user.sub, ); } catch (error) { if (error instanceof HttpException) throw error; throw new InternalServerErrorException( error instanceof Error ? error.message : "Failed to select outer parts", ); } } /** * V2 API: Select other damaged parts and provide bank information (Step 3 of claim workflow) */ @Patch("select-other-parts/:claimRequestId") @ApiOperation({ summary: "Select Other Parts & Bank Information (V2 - Step 3)", description: ` **Workflow Step:** SELECT_OTHER_PARTS (Step 3 of Claim) **Purpose:** User selects non-body damaged parts and provides bank information for payment. **Validations:** - Claim must exist - User must be the claim owner (damaged party from blame case) - Current workflow step must be SELECT_OTHER_PARTS - Bank information must not have been submitted previously - Sheba number must be exactly 24 digits - National code must be exactly 10 digits **Valid Other Parts (Optional):** - engine, suspension, brake_system, electrical - radiator, transmission, exhaust - headlight, taillight, mirror, glass **After Success:** - Workflow moves to: UPLOAD_REQUIRED_DOCUMENTS (Step 4) - User can proceed to upload required documents `, }) @ApiParam({ name: "claimRequestId", description: "The claim case ID (MongoDB ObjectId)", example: "507f1f77bcf86cd799439011", }) @ApiBody({ type: SelectOtherPartsV2Dto, description: "Other parts selection and bank information", examples: { example1: { summary: "Only bank info (no other parts damaged)", value: { otherParts: [], shebaNumber: "123456789012345678901234", nationalCodeOfOwner: "1234567890", }, }, example2: { summary: "Engine and suspension damage", value: { otherParts: ["engine", "suspension"], shebaNumber: "123456789012345678901234", nationalCodeOfOwner: "1234567890", }, }, example3: { summary: "Multiple systems damaged", value: { otherParts: ["engine", "brake_system", "electrical", "headlight"], shebaNumber: "123456789012345678901234", nationalCodeOfOwner: "1234567890", }, }, example4: { summary: "Lighting and glass damage", value: { otherParts: ["headlight", "taillight", "mirror", "glass"], shebaNumber: "123456789012345678901234", nationalCodeOfOwner: "1234567890", }, }, }, }) @ApiResponse({ status: 200, description: "Other parts and bank information saved successfully", type: SelectOtherPartsV2ResponseDto, }) @ApiResponse({ status: 400, description: "Invalid workflow step or validation failed", }) @ApiResponse({ status: 403, description: "User is not the claim owner", }) @ApiResponse({ status: 404, description: "Claim case not found", }) @ApiResponse({ status: 409, description: "Bank information already submitted", }) async selectOtherParts( @Param("claimRequestId") claimRequestId: string, @Body() body: SelectOtherPartsV2Dto, @CurrentUser() user: any, ): Promise { try { return await this.claimRequestManagementService.selectOtherPartsV2( claimRequestId, body, user.sub, ); } catch (error) { if (error instanceof HttpException) throw error; throw new InternalServerErrorException( error instanceof Error ? error.message : "Failed to select other parts", ); } } /** * V2 API: Get capture requirements (documents, angles, parts) */ @Get("capture-requirements/:claimRequestId") @ApiOperation({ summary: "Get Capture Requirements (V2)", description: ` **Get list of what needs to be captured:** - Required documents (13 items) - Car angles (4 items: front, back, left, right) - Damaged parts (based on selected outer parts) Returns status of each item (uploaded/captured or not). `, }) @ApiParam({ name: "claimRequestId", description: "The claim case ID (MongoDB ObjectId)", example: "507f1f77bcf86cd799439011", }) @ApiResponse({ status: 200, description: "Capture requirements retrieved successfully", type: GetCaptureRequirementsV2ResponseDto, }) @ApiResponse({ status: 403, description: "User is not the claim owner", }) @ApiResponse({ status: 404, description: "Claim case not found", }) async getCaptureRequirements( @Param("claimRequestId") claimRequestId: string, @CurrentUser() user: any, ): Promise { try { return await this.claimRequestManagementService.getCaptureRequirementsV2( claimRequestId, user.sub, ); } catch (error) { if (error instanceof HttpException) throw error; throw new InternalServerErrorException( error instanceof Error ? error.message : "Failed to get capture requirements", ); } } /** * V2 API: Upload required document */ @Post("upload-document/:claimRequestId") @UseInterceptors( FileInterceptor("file", { limits: { fileSize: 10 * 1024 * 1024, // 10MB }, storage: diskStorage({ destination: "./files/claim-documents", filename: (req, file, callback) => { const unique = Date.now(); const ex = extname(file.originalname); const filename = `${file.originalname.split(".")[0]}-${unique}${ex}`; callback(null, filename); }, }), }), ) @ApiConsumes("multipart/form-data") @ApiOperation({ summary: "Upload Required Document (V2 - Step 4)", description: ` **Workflow Step:** UPLOAD_REQUIRED_DOCUMENTS (Step 4 of Claim) **Upload one of the 13 required documents:** - car_green_card - damaged_driving_license_front/back - damaged_chassis_number, damaged_engine_photo - damaged_car_card_front/back, damaged_metal_plate - guilty_driving_license_front/back - guilty_car_card_front/back, guilty_metal_plate **When all 13 documents are uploaded:** - Workflow automatically moves to: CAPTURE_PART_DAMAGES (Step 5) `, }) @ApiParam({ name: "claimRequestId", description: "The claim case ID", example: "507f1f77bcf86cd799439011", }) @ApiBody({ schema: { type: "object", required: ["documentKey", "file"], properties: { documentKey: { type: "string", enum: [ "car_green_card", "damaged_driving_license_front", "damaged_driving_license_back", "damaged_chassis_number", "damaged_engine_photo", "damaged_car_card_front", "damaged_car_card_back", "damaged_metal_plate", "guilty_driving_license_front", "guilty_driving_license_back", "guilty_car_card_front", "guilty_car_card_back", "guilty_metal_plate", ], example: "car_green_card", }, file: { type: "string", format: "binary", }, }, }, }) @ApiResponse({ status: 200, description: "Document uploaded successfully", type: UploadRequiredDocumentV2ResponseDto, }) @ApiResponse({ status: 400, description: "Invalid workflow step", }) @ApiResponse({ status: 409, description: "Document already uploaded", }) async uploadDocument( @Param("claimRequestId") claimRequestId: string, @Body() body: UploadRequiredDocumentV2Dto, @UploadedFile() file: Express.Multer.File, @CurrentUser() user: any, ): Promise { try { return await this.claimRequestManagementService.uploadRequiredDocumentV2( claimRequestId, body, file, user.sub, ); } catch (error) { if (error instanceof HttpException) throw error; throw new InternalServerErrorException( error instanceof Error ? error.message : "Failed to upload document", ); } } /** * V2 API: Capture car angle or damaged part */ @Post("capture-part/:claimRequestId") @UseInterceptors( FileInterceptor("file", { limits: { fileSize: 10 * 1024 * 1024, // 10MB }, storage: diskStorage({ destination: "./files/claim-captures", filename: (req, file, callback) => { const unique = Date.now(); const ex = extname(file.originalname); const filename = `${file.originalname.split(".")[0]}-${unique}${ex}`; callback(null, filename); }, }), }), ) @ApiConsumes("multipart/form-data") @ApiOperation({ summary: "Capture Car Angle or Damaged Part (V2 - Step 5)", description: ` **Workflow Step:** CAPTURE_PART_DAMAGES (Step 5 of Claim) **Capture types:** 1. **angle**: Car angles (front, back, left, right) - 4 required 2. **part**: Damaged parts based on selectedParts from Step 2 **All captures must be completed before user can submit claim.** `, }) @ApiParam({ name: "claimRequestId", description: "The claim case ID", example: "507f1f77bcf86cd799439011", }) @ApiBody({ schema: { type: "object", required: ["captureType", "captureKey", "file"], properties: { captureType: { type: "string", enum: ["angle", "part"], example: "angle", }, captureKey: { type: "string", example: "front", description: 'For angle: front/back/left/right. For part: hood/front_bumper/etc.', }, file: { type: "string", format: "binary", }, }, }, }) @ApiResponse({ status: 200, description: "Capture saved successfully", type: CapturePartV2ResponseDto, }) @ApiResponse({ status: 400, description: "Invalid workflow step", }) async capturePart( @Param("claimRequestId") claimRequestId: string, @Body() body: CapturePartV2Dto, @UploadedFile() file: Express.Multer.File, @CurrentUser() user: any, ): Promise { try { return await this.claimRequestManagementService.capturePartV2( claimRequestId, body, file, user.sub, ); } catch (error) { if (error instanceof HttpException) throw error; throw new InternalServerErrorException( error instanceof Error ? error.message : "Failed to capture part", ); } } }