import {
  Body,
  Controller,
  Delete,
  Get,
  Headers,
  Param,
  ParseIntPipe,
  Patch,
  Post,
  Query,
  Req,
  Res,
  UploadedFile,
  UploadedFiles,
  UseGuards,
  UseInterceptors,
} from "@nestjs/common"
import {
  FileFieldsInterceptor,
  FileInterceptor,
} from "@nestjs/platform-express"
import {
  ApiBearerAuth,
  ApiBody,
  ApiConsumes,
  ApiHeader,
  ApiOperation,
  ApiParam,
  ApiQuery,
  ApiResponse,
  ApiTags,
} from "@nestjs/swagger"
import { customerUploadConfig } from "src/common/file-upload/customer-file-upload.config"
import { AuthGuardMiddleware } from "src/middleware/auth-guard.middleware"
import { CustomersService } from "./customers.service"
import { CreateCustomerDto, CustomerType } from "../dto/create-customer.dto"
import { SaveCustomerPhoneNumbersDto } from "../dto/customer-phone-numbers.dto"
import { FilterCustomerDto } from "../dto/filter-customer.dto"
import { UpdateCustomerDto } from "../dto/update-customer.dto"
import { AssignPricingCityByDispatcherDto } from "../dto/assign-pricing-city-by-dispatcher.dto"
import { CustomerMobileLoginDto } from "../dto/create-customer-mobile-login.dto"
import { CustomerAuthGuardMiddleware } from "src/middleware/customer-auth-guard.middleware"
import { CustomerRefreshTokenDto } from "../dto/customer-refresh-token.dto"
import { CreateCustomerEpisodeDto } from "../dto/customer-episode/create-customer-episode.dto"
import { UpdateCustomerEpisodeDto } from "../dto/customer-episode/update-customer-episode.dto"
import { CreateEpisodeLogDto } from "../dto/episode-log/create-episode-log.dto"
import { UpdateEpisodeLogDto } from "../dto/episode-log/update-episode-log.dto"
import { GenerateUploadUrlDto } from "../dto/episode-log/generate-upload-url.dto"
import { Request, Response } from "express"
@Controller("customers")
@ApiTags("Customers")
export class CustomersController {
  constructor(private readonly customersService: CustomersService) {}

  @Post()
  @UseGuards(AuthGuardMiddleware)
  @ApiBearerAuth("access-token")
  create(@Body() createCustomerDto: CreateCustomerDto) {
    return this.customersService.create(createCustomerDto)
  }

  @Post("backfill-place-ids")
  @UseGuards(AuthGuardMiddleware)
  @ApiBearerAuth("access-token")
  @ApiOperation({
    summary: "Backfill Place IDs",
    description:
      "Fetches Google Place IDs for customers that have a primary address but no place_id, using the Geocoding API.",
  })
  backfillPlaceIds() {
    return this.customersService.backfillPlaceIds()
  }

  @Post("assign-pricing-city-by-dispatcher")
  @UseGuards(AuthGuardMiddleware)
  @ApiBearerAuth("access-token")
  @ApiOperation({
    summary: "Assign pricing city to all customers of a dispatcher",
    description:
      "Sets the same pricing city on every customer whose dispatcher_id matches the given dispatcher.",
  })
  @ApiResponse({
    status: 200,
    description: "Pricing city assigned successfully.",
  })
  @ApiResponse({
    status: 400,
    description: "Validation error or entity not found.",
  })
  assignPricingCityByDispatcher(@Body() dto: AssignPricingCityByDispatcherDto) {
    return this.customersService.assignPricingCityByDispatcher(dto)
  }

  @Get()
  @UseGuards(AuthGuardMiddleware)
  @ApiBearerAuth("access-token")
  findAll(@Req() request: any, @Query() filterCustomerDto: FilterCustomerDto) {
    return this.customersService.findAll(
      request.headers["authorization"],
      filterCustomerDto,
    )
  }

  // @Post("phone-number/:customer_id")
  // async addPhoneNumber(
  //   @Param("customer_id", ParseIntPipe) customer_id: number,
  //   @Body() phoneNumberDto: SaveCustomerPhoneNumbersDto,
  // ) {
  //   return await this.customersService.addPhoneNumber(
  //     customer_id,
  //     phoneNumberDto,
  //   )
  // }

  @Get("phone-number/:customer_id")
  @UseGuards(AuthGuardMiddleware)
  @ApiBearerAuth("access-token")
  async getPhoneNumbers(
    @Param("customer_id", ParseIntPipe) customer_id: number,
  ) {
    return await this.customersService.getPhoneNumbers(customer_id)
  }

  @Patch("phone-number/:customer_id")
  @UseGuards(AuthGuardMiddleware)
  @ApiBearerAuth("access-token")
  async updatePhoneNumber(
    @Param("customer_id", ParseIntPipe) customer_id: number,
    @Body() phoneNumberDto: SaveCustomerPhoneNumbersDto,
  ) {
    return await this.customersService.updatePhoneNumber(
      customer_id,
      phoneNumberDto,
    )
  }

  @Get("tags/:type")
  @UseGuards(AuthGuardMiddleware)
  @ApiBearerAuth("access-token")
  @ApiParam({
    name: "type",
    required: true,
    type: String,
    description: "Type of the tag (e.g., customer, driver, etc.)",
  })
  async getTags(@Param("type") type: any) {
    return await this.customersService.getTags(type)
  }

  @Get("mobile-app/:id")
  @UseGuards(CustomerAuthGuardMiddleware)
  @ApiBearerAuth("access-token")
  findOneForMobileApp(@Param("id") id: string) {
    return this.customersService.findOne(+id, true)
  }

  @Patch("mobile-app/:id")
  @UseGuards(CustomerAuthGuardMiddleware)
  @ApiBearerAuth("access-token")
  @ApiConsumes("multipart/form-data")
  @ApiBody({
    description: "Update customer with optional file uploads",
    schema: {
      type: "object",
      properties: {
        profile_photo: { type: "string", format: "binary", nullable: true },
        customer_name: { type: "string" },
        date_of_birth: { type: "string", format: "date" },
        gender: { type: "string" },
        current_step: { type: "integer" },
      },
    },
  })
  @UseInterceptors(
    FileFieldsInterceptor(
      [
        { name: "profile_photo", maxCount: 1 },
        { name: "letter_of_guarantee", maxCount: 1 },
      ],
      customerUploadConfig,
    ),
  )
  async updateForMobileApp(
    @Param("id") id: string,
    @Body() updateCustomerDto: UpdateCustomerDto,
    @UploadedFiles()
    files: {
      profile_photo?: Express.Multer.File[]
      letter_of_guarantee?: Express.Multer.File[]
    },
  ) {
    return this.customersService.update(
      +id,
      updateCustomerDto,
      {
        profile_photo: files.profile_photo?.[0],
        letter_of_guarantee: files.letter_of_guarantee?.[0],
      },
      true,
    )
  }

  @Get(":id")
  @UseGuards(AuthGuardMiddleware)
  @ApiBearerAuth("access-token")
  findOne(@Param("id") id: string) {
    return this.customersService.findOne(+id, false)
  }

  @Patch(":id")
  @UseGuards(AuthGuardMiddleware)
  @ApiBearerAuth("access-token")
  @ApiConsumes("multipart/form-data")
  @ApiBody({
    description: "Update customer with optional file uploads",
    schema: {
      type: "object",
      properties: {
        profile_photo: { type: "string", format: "binary", nullable: true },
        letter_of_guarantee: {
          type: "string",
          format: "binary",
          nullable: true,
        },
        customer_name: { type: "string" },
        customer_type: {
          type: "string",
          enum: Object.values(CustomerType),
          nullable: true,
        },
        date_of_birth: { type: "string", format: "date" },
        gender: { type: "string" },
        primary_address: { type: "string" },
        secondary_address: { type: "string" },
        zip_code: { type: "string" },
        visa_start_date: { type: "string", format: "date" },
        visa_end_date: { type: "string", format: "date" },
        pricing_city_id: { type: "string" },
        country_code: { type: "string" },
        phone_number: { type: "string" },
        place_id: { type: "string" },
        email: { type: "string", format: "email" },
        tags: {
          type: "array",
          items: { type: "integer" },
          description: "Array of tag IDs",
        },
        reference_number: { type: "string" },
        prn_number: { type: "string" },
        episode: { type: "string" },
        log_issued_at: { type: "string", format: "date" },
        log_expire_at: { type: "string", format: "date" },
        department: { type: "string" },
        job_title: { type: "string" },
        status: { type: "string" },
        current_step: { type: "integer" },
        client_company_id: { type: "integer", nullable: true },
        dispatcher_id: { type: "integer", nullable: true },
        client_contact_id: { type: "integer", nullable: true },
        city_id: { type: "integer", nullable: true },
        state_id: { type: "integer", nullable: true },
        country_id: { type: "integer", nullable: true },
        hospital_id: { type: "integer", nullable: true },
        vehicle_type_id: { type: "integer", nullable: true },
      },
    },
  })
  @UseInterceptors(
    FileFieldsInterceptor(
      [
        { name: "profile_photo", maxCount: 1 },
        { name: "letter_of_guarantee", maxCount: 1 },
      ],
      customerUploadConfig,
    ),
  )
  async update(
    @Param("id") id: string,
    @Body() updateCustomerDto: UpdateCustomerDto,
    @UploadedFiles()
    files: {
      profile_photo?: Express.Multer.File[]
      letter_of_guarantee?: Express.Multer.File[]
    },
  ) {
    return this.customersService.update(+id, updateCustomerDto, {
      profile_photo: files.profile_photo?.[0],
      letter_of_guarantee: files.letter_of_guarantee?.[0],
    })
  }

  @Delete(":id")
  @UseGuards(AuthGuardMiddleware)
  @ApiBearerAuth("access-token")
  remove(@Param("id") id: string) {
    return this.customersService.remove(+id)
  }

  @Get("/count/ratings/:customer_id")
  @UseGuards(AuthGuardMiddleware)
  @ApiBearerAuth("access-token")
  @ApiOperation({ summary: "Get ratings count" })
  async getRatingsCount(@Param("customer_id") customer_id: number) {
    return this.customersService.getRating(customer_id)
  }

  @Post("customer-mobile-login")
  @ApiOperation({ summary: "Mobile Login with Email or Contact No" })
  @ApiHeader({
    name: "device_token",
    description: "Device token for push notification",
    required: false,
  })
  @ApiBody({
    schema: {
      type: "object",
      properties: {
        email: {
          type: "string",
          example: "user@example.com",
          description: "Email (optional if contact_no provided)",
        },
        country_code: {
          type: "string",
          example: "+91",
          description: "Country code required if using contact_no",
        },
        phone_number: {
          type: "string",
          example: "9876543210",
          description: "Contact Number (optional if email provided)",
        },
        device_id: {
          type: "string",
          example: "abc123xyz",
          description: "Device ID",
        },
        device_type: {
          type: "string",
          example: "android",
          description: "Device Type (android/ios)",
        },
        os_version: {
          type: "string",
          example: "13.0",
          description: "OS Version",
        },
        fcm_token: {
          type: "string",
          example: "abc123xyz",
          description: "FCM Token",
        },
      },
      required: [],
    },
  })
  mobileLogin(
    @Body() mobileLoginDto: CustomerMobileLoginDto,
    @Req() request: Request,
  ) {
    const deviceToken = request.headers["device_token"] as string
    mobileLoginDto.device_token = deviceToken
    return this.customersService.customerMobileLogin(mobileLoginDto)
  }

  // ============================================
  // Controller - Customer Verify OTP (FCM from HEADER)
  // ============================================

  @Post("customer-verify-otp")
  @ApiOperation({ summary: "Verify OTP with expiry check (1 minute)" })
  @ApiResponse({ status: 200, description: "OTP verification result" })
  @ApiBody({
    schema: {
      type: "object",
      properties: {
        user_id: {
          type: "number",
          example: 1,
          description: "User ID",
        },
        otp: {
          type: "string",
          example: "123456",
          description: "OTP code",
        },
      },
      required: ["otp", "user_id"],
    },
  })
  @ApiHeader({
    name: "fcm-token",
    description: "FCM Token for push notifications",
    required: false,
    schema: {
      type: "string",
      example: "abc123xyz",
    },
  })
  async verifyOtp(
    @Body() body: { otp: string; user_id: number },
    @Headers("fcm-token") fcmToken?: string,
  ) {
    return this.customersService.verifyOtp(body.otp, body.user_id, fcmToken)
  }

  @Post("customer-logout")
  @UseGuards(CustomerAuthGuardMiddleware)
  @ApiBearerAuth("access-token")
  logout(@Req() request: Request) {
    return this.customersService.customerLogout(
      request.headers["authorization"],
    )
  }

  @Post("customer-refresh-token")
  @ApiOperation({ summary: "Refresh Access Token using Refresh Token" })
  @ApiResponse({ status: 200, description: "New access token generated" })
  @ApiBody({
    description: "Refresh token to get new access token",
    type: CustomerRefreshTokenDto,
  })
  async refreshToken(@Body() refreshTokenDto: CustomerRefreshTokenDto) {
    return this.customersService.refreshCustomerAccessToken(
      refreshTokenDto.refresh_token,
    )
  }

  // Customer Episode Routes
  @Post(":id/episodes")
  @UseGuards(AuthGuardMiddleware)
  @ApiBearerAuth("access-token")
  @ApiOperation({ summary: "Create a customer episode" })
  @ApiResponse({
    status: 201,
    description: "Customer episode created successfully.",
  })
  createCustomerEpisode(
    @Param("id", ParseIntPipe) customerId: number,
    @Body() createCustomerEpisodeDto: CreateCustomerEpisodeDto,
  ) {
    return this.customersService.createCustomerEpisode(
      customerId,
      createCustomerEpisodeDto,
    )
  }

  @Get(":id/episodes")
  @UseGuards(AuthGuardMiddleware)
  @ApiBearerAuth("access-token")
  @ApiOperation({ summary: "Get all episodes for a customer" })
  @ApiResponse({
    status: 200,
    description: "Customer episodes retrieved successfully.",
  })
  findAllCustomerEpisodes(@Param("id", ParseIntPipe) customerId: number) {
    return this.customersService.findAllCustomerEpisodes(customerId)
  }

  @Get("episodes/:episodeId")
  @UseGuards(AuthGuardMiddleware)
  @ApiBearerAuth("access-token")
  @ApiOperation({ summary: "Get a customer episode by ID" })
  @ApiResponse({
    status: 200,
    description: "Customer episode retrieved successfully.",
  })
  findOneCustomerEpisode(@Param("episodeId", ParseIntPipe) episodeId: number) {
    return this.customersService.findOneCustomerEpisode(episodeId)
  }

  @Patch("episodes/:episodeId")
  @UseGuards(AuthGuardMiddleware)
  @ApiBearerAuth("access-token")
  @ApiOperation({ summary: "Update a customer episode" })
  @ApiResponse({
    status: 200,
    description: "Customer episode updated successfully.",
  })
  updateCustomerEpisode(
    @Param("episodeId", ParseIntPipe) episodeId: number,
    @Body() updateCustomerEpisodeDto: UpdateCustomerEpisodeDto,
  ) {
    return this.customersService.updateCustomerEpisode(
      episodeId,
      updateCustomerEpisodeDto,
    )
  }

  @Delete("episodes/:episodeId")
  @UseGuards(AuthGuardMiddleware)
  @ApiBearerAuth("access-token")
  @ApiOperation({ summary: "Delete a customer episode" })
  @ApiResponse({
    status: 200,
    description: "Customer episode deleted successfully.",
  })
  removeCustomerEpisode(@Param("episodeId", ParseIntPipe) episodeId: number) {
    return this.customersService.removeCustomerEpisode(episodeId)
  }

  // Episode Log Routes
  @Post("episodes/:episodeId/logs/upload-url")
  @UseGuards(AuthGuardMiddleware)
  @ApiBearerAuth("access-token")
  @ApiOperation({
    summary: "Get presigned URL for uploading episode log PDF document",
  })
  @ApiResponse({
    status: 200,
    description: "Presigned upload URL generated successfully.",
  })
  getEpisodeLogUploadUrl(
    @Param("episodeId", ParseIntPipe) episodeId: number,
    @Body() generateUploadUrlDto: GenerateUploadUrlDto,
  ) {
    return this.customersService.generateEpisodeLogUploadUrl(
      episodeId,
      generateUploadUrlDto,
    )
  }

  @Post("episodes/:episodeId/logs")
  @UseGuards(AuthGuardMiddleware)
  @ApiBearerAuth("access-token")
  @ApiOperation({ summary: "Create an episode log with PDF document URL" })
  @ApiResponse({
    status: 201,
    description: "Episode log created successfully.",
  })
  createEpisodeLog(
    @Param("episodeId", ParseIntPipe) episodeId: number,
    @Body() createEpisodeLogDto: CreateEpisodeLogDto,
  ) {
    console.log(episodeId, "episodeId")
    return this.customersService.createEpisodeLog(
      episodeId,
      createEpisodeLogDto,
    )
  }

  @Get("episodes/:episodeId/logs")
  @UseGuards(AuthGuardMiddleware)
  @ApiBearerAuth("access-token")
  @ApiOperation({ summary: "Get all logs for an episode" })
  @ApiResponse({
    status: 200,
    description: "Episode logs retrieved successfully.",
  })
  findAllEpisodeLogs(@Param("episodeId", ParseIntPipe) episodeId: number) {
    return this.customersService.findAllEpisodeLogs(episodeId)
  }

  @Get("logs/:logId")
  @UseGuards(AuthGuardMiddleware)
  @ApiBearerAuth("access-token")
  @ApiOperation({ summary: "Get an episode log by ID" })
  @ApiResponse({
    status: 200,
    description: "Episode log retrieved successfully.",
  })
  findOneEpisodeLog(@Param("logId", ParseIntPipe) logId: number) {
    return this.customersService.findOneEpisodeLog(logId)
  }

  @Patch("logs/:logId")
  @UseGuards(AuthGuardMiddleware)
  @ApiBearerAuth("access-token")
  @ApiOperation({ summary: "Update an episode log with PDF document URL" })
  @ApiResponse({
    status: 200,
    description: "Episode log updated successfully.",
  })
  updateEpisodeLog(
    @Param("logId", ParseIntPipe) logId: number,
    @Body() updateEpisodeLogDto: UpdateEpisodeLogDto,
  ) {
    return this.customersService.updateEpisodeLog(logId, updateEpisodeLogDto)
  }

  @Delete("logs/:logId")
  @UseGuards(AuthGuardMiddleware)
  @ApiBearerAuth("access-token")
  @ApiOperation({ summary: "Delete an episode log" })
  @ApiResponse({
    status: 200,
    description: "Episode log deleted successfully.",
  })
  removeEpisodeLog(@Param("logId", ParseIntPipe) logId: number) {
    return this.customersService.removeEpisodeLog(logId)
  }

  @Get("active/episode/:customerId")
  @UseGuards(AuthGuardMiddleware)
  @ApiBearerAuth("access-token")
  @ApiOperation({
    summary: "Get Active Customer Episode with Logs (Paginated + Search)",
  })
  @ApiQuery({ name: "page", required: false, type: Number, example: 1 })
  @ApiQuery({ name: "limit", required: false, type: Number, example: 10 })
  @ApiQuery({ name: "search", required: false, type: String, example: "fever" })
  getActiveEpisode(
    @Param("customerId", ParseIntPipe) customerId: number,
    @Query("skip") skip: number = 0,
    @Query("limit") limit: number = 10,
    @Query("search") search?: string,
  ) {
    return this.customersService.getActiveEpisodeWithLogs(
      customerId,
      Number(skip) || 0,
      Number(limit) || 10,
      search,
    )
  }

  @Get(":episode_id/invoice-status")
  @UseGuards(AuthGuardMiddleware)
  @ApiBearerAuth("access-token")
  @ApiOperation({ summary: "Get an episode log by ID" })
  @ApiResponse({
    status: 200,
    description: "Episode log retrieved successfully.",
  })
  checkEpisodeInvoiceStatus(
    @Param("episode_id", ParseIntPipe) episodeId: number,
  ) {
    return this.customersService.checkEpisodeInvoiceStatus(episodeId)
  }

  @Post("bulk-upload")
  @UseGuards(AuthGuardMiddleware)
  @ApiOperation({ summary: "Bulk upload customer Excel file" })
  @ApiConsumes("multipart/form-data")
  @ApiBody({
    description: "Excel file to upload",
    schema: {
      type: "object",
      properties: {
        file: {
          type: "string",
          format: "binary",
        },
      },
    },
  })
  @ApiResponse({
    status: 200,
    description: "Bulk upload processed successfully",
  })
  @ApiResponse({
    status: 400,
    description: "Validation or data processing error",
  })
  @UseInterceptors(FileInterceptor("file"))
  async bulkUpload(
    @UploadedFile() file: Express.Multer.File,
    @Res() res: Response,
  ) {
    const result: any = await this.customersService.bulkUpload(file)

    if (Buffer.isBuffer(result)) {
      res.status(207)
      res.setHeader(
        "Content-Type",
        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
      )
      res.setHeader(
        "Content-Disposition",
        `attachment; filename="bulk-upload-errors-${Date.now()}.xlsx"`,
      )
      return res.send(result)
    }

    return res.status(200).json({
      success: true,
      message: result.message,
    })
  }
}
