import { Injectable } from "@nestjs/common"
import { ConfigService } from "@nestjs/config"
import { failureResponse, successResponse } from "src/common/response/response"
import { code } from "src/common/response/response.code"
import { messageKey } from "src/constants/message-keys"
import {
  errorMessage,
  isEmpty,
  successMessage,
  validationMessage,
} from "src/utils/helpers"
import { InspectionQuestionsRepository } from "../repositories/inspection-questions.repository"
import { CreateInspectionQuestionDto } from "../dto/create-inspection-questions.dto"
import { InspectionQuestion } from "../entities/inspection-questions.entity"
import { FilterInspectionQuestionDto } from "../dto/filter-inspection-questions.dto"
import { UpdateInspectionQuestionsDto } from "../dto/update-inspection-questions-dto"
import { InspectionAnswerOption } from "../entities/inspection-answer-option.entity"
import { instanceToPlain } from "class-transformer"
import { CreateInspectionReportWithAnswersDto } from "../dto/save-inspection-answers.dto"
import { InspectionFleetReport } from "../entities/inspection-fleet-report.entity"
import { InspectionFleetAnswer } from "../entities/inspection-report-answers.entity"
import { InspectionAnswerOptionRepository } from "../repositories/inspection-answer-options.repository"
import { InspectionFleetReportRepository } from "../repositories/inspection-fleet-report.repository"
import { InspectionFleetAnswerRepository } from "../repositories/inspection-fleet-answer.repository"
import { InjectRepository } from "@nestjs/typeorm"
import { Repository } from "typeorm"
import { FilterInspectionReportDto } from "../dto/filter-inspection-report.dto"

@Injectable()
export class InspectionQuestionsService {
  constructor(
    private readonly configService: ConfigService,
    private readonly inspectionQuestionRepository: InspectionQuestionsRepository,
    private readonly inspectionAnswerOptionRepository: InspectionAnswerOptionRepository,
    private readonly inspectionFleetReportRepository: InspectionFleetReportRepository,
    private readonly inspectionFleetAnswerRepository: InspectionFleetAnswerRepository,
    @InjectRepository(InspectionFleetReport)
    private inspectionFleetReportModuleRepository: Repository<InspectionFleetReport>,
  ) {}

  async create(createInspectionQuestionDto: CreateInspectionQuestionDto) {
    // Check for duplicate question
    const checkUniqueQuestion =
      await this.inspectionQuestionRepository.getByParams({
        where: {
          question_text: createInspectionQuestionDto?.question_text,
        },
        findOne: true,
      })

    if (!isEmpty(checkUniqueQuestion)) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.already_exist, {
          ":data": "Question Text",
        }),
      )
    }

    const question = new InspectionQuestion()
    question.question_text = createInspectionQuestionDto.question_text

    // Map answer option strings to entity objects
    question.answer_options = createInspectionQuestionDto.answer_options.map(
      (text) => {
        const option = new InspectionAnswerOption()
        option.option_text = text
        return option
      },
    )

    const savedQuestion = await this.inspectionQuestionRepository.save(question)

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_add, { ":data": "Inspection Question" }),
      savedQuestion,
    )
  }

  async findAll(filterInspectionQuestionDto: FilterInspectionQuestionDto) {
    const defaultPagination = {
      take: this.configService.get<number>("APP.pagination.take"),
      skip: this.configService.get<number>("APP.pagination.skip"),
    }

    const { search, limit, skip, sortBy, sortOrder } =
      filterInspectionQuestionDto

    const validSortColumns = ["id", "question_text", "created_at", "updated_at"]

    let sortColumn: string = "created_at"
    const order: "ASC" | "DESC" = sortOrder === "ASC" ? "ASC" : "DESC"

    if (sortBy && validSortColumns.includes(sortBy)) {
      sortColumn = sortBy
    }

    const queryParams: any = {
      take: Number(limit) || defaultPagination.take,
      skip: Number(skip) || defaultPagination.skip,
      orderBy: { [sortColumn]: order },
      relations: ["answer_options"],
      where: {},
    }

    if (search) {
      queryParams.search = {
        question_text: search,
      }
    }

    const questions =
      await this.inspectionQuestionRepository.getByParams(queryParams)

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_retrieve, {
        ":data": "Inspection Questions",
      }),
      questions,
    )
  }

  async findOne(id: number) {
    const question = await this.inspectionQuestionRepository.getByParams({
      where: { id },
      findOne: true,
      relations: ["answer_options"],
    })

    if (isEmpty(question)) {
      return failureResponse(
        code.DATA_NOT_FOUND,
        errorMessage(messageKey.data_not_found, {
          ":data": "Inspection Question",
        }),
      )
    }

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_retrieve, {
        ":data": "Inspection Question",
      }),
      question,
    )
  }

  async update(
    id: number,
    updateInspectionQuestionDto: UpdateInspectionQuestionsDto,
  ) {
    const question = await this.inspectionQuestionRepository.getByParams({
      where: { id },
      relations: ["answer_options"],
      findOne: true,
    })

    if (isEmpty(question)) {
      return failureResponse(
        code.DATA_NOT_FOUND,
        errorMessage(messageKey.data_not_found, {
          ":data": "Inspection Question",
        }),
      )
    }

    // Check for duplicate question text (excluding self)
    if (updateInspectionQuestionDto?.question_text) {
      const existingQuestion =
        (await this.inspectionQuestionRepository.getByParams({
          where: { question_text: updateInspectionQuestionDto.question_text },
          findOne: true,
        })) as any

      if (!isEmpty(existingQuestion) && existingQuestion.id !== id) {
        return failureResponse(
          code.VALIDATION,
          validationMessage(messageKey.already_exist, {
            ":data": "Question Text",
          }),
        )
      }
    }

    const existing = question as any
    existing.question_text = updateInspectionQuestionDto.question_text

    const incomingOptions = updateInspectionQuestionDto.answer_options || []

    const oldOptionsMap = new Map(
      existing.answer_options.map((opt) => [opt.id, opt]),
    )

    const finalOptions = incomingOptions.map((opt) => {
      if (opt.id && oldOptionsMap.has(opt.id)) {
        const old = oldOptionsMap.get(opt.id) as InspectionAnswerOption
        old.option_text = opt.option_text
        oldOptionsMap.delete(opt.id)
        return old
      } else {
        const newOpt = new InspectionAnswerOption()
        newOpt.option_text = opt.option_text
        newOpt.question = existing
        return newOpt
      }
    })

    const toDelete = Array.from(
      oldOptionsMap.values(),
    ) as InspectionAnswerOption[]
    if (toDelete.length > 0) {
      for (const option of toDelete) {
        await this.inspectionAnswerOptionRepository.remove({ id: option.id })
      }
    }

    existing.answer_options = finalOptions

    const updatedQuestion =
      await this.inspectionQuestionRepository.save(existing)

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_update, {
        ":data": "Inspection Question",
      }),
      instanceToPlain(updatedQuestion),
    )
  }

  async remove(id: number) {
    const question = await this.inspectionQuestionRepository.getByParams({
      where: { id },
      findOne: true,
    })

    if (isEmpty(question)) {
      return failureResponse(
        code.DATA_NOT_FOUND,
        errorMessage(messageKey.data_not_found, {
          ":data": "Inspection Question",
        }),
      )
    }

    await this.inspectionQuestionRepository.remove({ id }, null, false)

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_removed, {
        ":data": "Inspection Question",
      }),
    )
  }

  async removeOption(questionId: number, optionId: number) {
    const option = await this.inspectionAnswerOptionRepository.getByParams({
      where: {
        id: optionId,
        question_id: questionId,
      },
      findOne: true,
    })

    if (isEmpty(option)) {
      return failureResponse(
        code.DATA_NOT_FOUND,
        errorMessage(messageKey.data_not_found, { ":data": "Answer Option" }),
      )
    }

    await this.inspectionAnswerOptionRepository.remove({ id: optionId })

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_removed, {
        ":data": "Inspection Answer Option",
      }),
    )
  }

  async createReportWithAnswers(
    createReportDto: CreateInspectionReportWithAnswersDto,
  ) {
    const { fleet_id, driver_id, answers } = createReportDto

    const report = new InspectionFleetReport()
    report.fleet_id = fleet_id
    report.driver_id = driver_id

    const savedReport = await this.inspectionFleetReportRepository.save(report)

    const answerEntities: InspectionFleetAnswer[] = answers.map((ans) => {
      const answer = new InspectionFleetAnswer()
      answer.inspection_fleet_report_id = savedReport.id
      answer.question_id = ans.question_id
      answer.selected_answer_id = ans.selected_answer_id
      return answer
    })

    for (const answerEntity of answerEntities) {
      await this.inspectionFleetAnswerRepository.save(answerEntity)
    }

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_add, { ":data": "Inspection Report" }),
      { inspection_report_id: savedReport.id },
    )
  }

  // async getAllInspectionReports(limit?: string, skip?: string) {
  //   const defaultPagination = {
  //     take: this.configService.get<number>("APP.pagination.take"),
  //     skip: this.configService.get<number>("APP.pagination.skip"),
  //   }

  //   const take = Number(limit) || defaultPagination.take
  //   const offset = Number(skip) || defaultPagination.skip

  //   const queryParams: any = {
  //     take,
  //     skip: offset,
  //     orderBy: { created_at: "DESC" },
  //     relations: [
  //       "fleet:id,registration_number",
  //       "driver:id,first_name,last_name,country_code,phone_number.reporting_to:id,first_name,last_name,country_code,phone_number",
  //     ],
  //     select: ["id", "created_at"],
  //   }

  //   const reports =
  //     await this.inspectionFleetReportRepository.getByParams(queryParams)

  //   return successResponse(
  //     code.SUCCESS,
  //     successMessage(messageKey.data_retrieve, {
  //       ":data": "Inspection Reports",
  //     }),
  //     reports,
  //   )
  // }

  async getAllInspectionReports(
    filterInspectionReportDto: FilterInspectionReportDto,
  ) {
    const defaultPagination = {
      take: this.configService.get("APP.pagination.take"),
      skip: this.configService.get("APP.pagination.skip"),
    }

    const search: any = {}
    if (filterInspectionReportDto.search) {
      search["fleet.registration_number"] = filterInspectionReportDto.search
      search["driver.first_name"] = filterInspectionReportDto.search
      search["driver.last_name"] = filterInspectionReportDto.search
      search["driver.phone_number"] = filterInspectionReportDto.search
      search["driver.reporting_to.first_name"] =
        filterInspectionReportDto.search
      search["driver.reporting_to.last_name"] = filterInspectionReportDto.search
      search["driver.reporting_to.phone_number"] =
        filterInspectionReportDto.search
      search["created_at"] = filterInspectionReportDto.search
    }

    const filters: any = {}
    if (filterInspectionReportDto?.fleet_id) {
      filters["fleet_id"] = filterInspectionReportDto.fleet_id
        .toString()
        .split(",")
    }

    const queryParams: any = {
      take: filterInspectionReportDto?.limit || defaultPagination.take,
      skip: filterInspectionReportDto?.skip || defaultPagination.skip,
      search,
      whereIn: filters,
      orderBy: { created_at: "DESC" },
      relations: [
        "fleet:id,registration_number",
        "driver:id,first_name,last_name,country_code,phone_number,profile_photo,reporting_to_id.reporting_to:id,first_name,last_name,country_code,phone_number",
      ],
      select: ["id", "created_at", "updated_at"],
    }

    const reports =
      await this.inspectionFleetReportRepository.getByParams(queryParams)

    if (isEmpty(reports)) {
      return failureResponse(
        code.SUCCESS,
        errorMessage(messageKey.data_not_found, {
          ":data": "Inspection Reports",
        }),
      )
    }

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_retrieve, {
        ":data": "Inspection Reports",
      }),
      reports,
    )
  }

  async getInspectionReport(id: number) {
    const report = (await this.inspectionFleetReportModuleRepository.findOne({
      where: { id },
      relations: [
        "fleet",
        "driver",
        "driver.reporting_to",
        "inspection_answers",
        "inspection_answers.question",
        "inspection_answers.question.answer_options",
        "inspection_answers.selected_answer",
      ],
      select: [
        "id",
        "back_side_photo",
        "front_side_photo",
        "left_side_photo",
        "right_side_photo",
        "created_at",
      ],
    })) as InspectionFleetReport

    if (isEmpty(report)) {
      return failureResponse(
        code.DATA_NOT_FOUND,
        errorMessage(messageKey.data_not_found, {
          ":data": "Inspection Report",
        }),
      )
    }

    const baseUrl = process.env.BACK_URL || "http://localhost:5000"
    const formatImage = (path?: string) => (path ? `${baseUrl}/${path}` : null)

    const reportData = report as any

    const customResponse = {
      id: reportData.id,
      fleet: {
        id: reportData.fleet?.id,
        registration_number: reportData.fleet?.registration_number,
      },
      driver: {
        id: reportData.driver?.id,
        first_name: reportData.driver?.first_name,
        last_name: reportData.driver?.last_name,
        country_code: reportData.driver?.country_code,
        phone_number: reportData.driver?.phone_number,
        reporting_to: reportData.driver?.reporting_to
          ? {
              id: reportData.driver.reporting_to.id,
              first_name: reportData.driver.reporting_to.first_name,
              last_name: reportData.driver.reporting_to.last_name,
              country_code: reportData.driver.reporting_to.country_code,
              phone_number: reportData.driver.reporting_to.phone_number,
            }
          : null,
      },
      inspection_answers: reportData.inspection_answers.map((ans) => ({
        question: ans.question?.question_text,
        selected_answer: ans.selected_answer?.option_text || null,
        options:
          ans.question?.answer_options?.map((opt) => ({
            id: opt.id,
            option_text: opt.option_text,
            selected: ans.selected_answer?.id === opt.id,
          })) || [],
      })),
      front_side_photo: formatImage(reportData.front_side_photo),
      right_side_photo: formatImage(reportData.right_side_photo),
      back_side_photo: formatImage(reportData.back_side_photo),
      left_side_photo: formatImage(reportData.left_side_photo),
      created_at: reportData.created_at,
    }

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_retrieve, {
        ":data": "Inspection Report",
      }),
      customResponse,
    )
  }

  async uploadInspectionReportPhotos(
    id: number,
    photos?: {
      front_side_photo?: Express.Multer.File[]
      right_side_photo?: Express.Multer.File[]
      back_side_photo?: Express.Multer.File[]
      left_side_photo?: Express.Multer.File[]
    },
  ) {
    try {
      const report = (await this.inspectionFleetReportRepository.getByParams({
        where: { id },
        findOne: true,
      })) as any

      if (isEmpty(report)) {
        return failureResponse(
          code.DATA_NOT_FOUND,
          errorMessage(messageKey.data_not_found, {
            ":data": "Inspection Report",
          }),
        )
      }

      // clean file paths
      const cleanPath = (file?: Express.Multer.File[]) =>
        file?.[0]?.path?.replace(/\\/g, "/").replace(/^public\//, "")

      if (photos?.front_side_photo?.[0]) {
        report.front_side_photo = cleanPath(photos.front_side_photo)
      }

      if (photos?.right_side_photo?.[0]) {
        report.right_side_photo = cleanPath(photos.right_side_photo)
      }

      if (photos?.back_side_photo?.[0]) {
        report.back_side_photo = cleanPath(photos.back_side_photo)
      }

      if (photos?.left_side_photo?.[0]) {
        report.left_side_photo = cleanPath(photos.left_side_photo)
      }

      await this.inspectionFleetReportRepository.save(report)

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_update, {
          ":data": "Inspection Report Photos",
        }),
      )
    } catch (error) {
      return failureResponse(code.ERROR, messageKey.something_went_wrong)
    }
  }

  async checkDataExist(
    questionText: string,
    field: string,
    excludeId?: number,
  ): Promise<boolean> {
    const whereCondition: any = {}
    const whereLikeCondition: any = {}

    whereLikeCondition[field] = questionText

    if (excludeId) {
      whereCondition["id"] = {
        not: excludeId,
      }
    }

    const question = await this.inspectionQuestionRepository.getByParams({
      where: whereCondition,
      whereLower: whereLikeCondition,
      findOne: true,
    })

    return !isEmpty(question)
  }
}
