import { Injectable } from "@nestjs/common"
import { CreateGeneralLandingPageDto } from "./dto/create-general-landing-page.dto"
import { UpdateGeneralLandingPageDto } from "./dto/update-general-landing-page.dto"
import { GeneralLandingPageFilterDto } from "./dto/general-landing-page-filter.dto"
import { successResponse, failureResponse } from "src/common/response/response"
import { code } from "src/common/response/response.code"
import {
  successMessage,
  errorMessage,
  validationMessage,
} from "src/utils/helpers"
import { messageKey } from "src/constants/message-keys"
import { isEmpty } from "class-validator"
import { GeneralLandingPageRepository } from "./repository/general-landing-page.repository"
import {
  SECTION,
  SECTION_VALUES,
  NEXT_SCREEN_VALUES,
} from "src/constants/general-landing-page.constant"
import { ReorderGeneralLandingPageDto } from "./dto/reorder-general-landing-page.dto"

@Injectable()
export class GeneralLandingPageService {
  constructor(
    private readonly generalLandingPageRepository: GeneralLandingPageRepository,
  ) {}

  private async getNextSortOrder(): Promise<number> {
    const records: any = await this.generalLandingPageRepository.getByParams({
      orderBy: { sort_order: "DESC" },
    })
    const list = Array.isArray(records) ? records : []
    if (!list.length) {
      return 1
    }
    return (Number(list[0].sort_order) || 0) + 1
  }

  private async shiftSortOrdersUp() {
    const records: any = await this.generalLandingPageRepository.getByParams({
      orderBy: { sort_order: "DESC" },
    })
    const list = Array.isArray(records) ? records : []

    for (const item of list) {
      await this.generalLandingPageRepository.save({
        id: item.id,
        sort_order: Number(item.sort_order) + 1,
      })
    }
  }

  private async resequenceSortOrder() {
    const records: any = await this.generalLandingPageRepository.getByParams({
      orderBy: { sort_order: "ASC" },
    })
    const list = Array.isArray(records) ? records : []
    const header = list.find((item) => item.section === SECTION.HEADER)
    const listings = list.filter((item) => item.section !== SECTION.HEADER)

    if (header) {
      await this.generalLandingPageRepository.save({
        id: header.id,
        sort_order: 1,
      })

      for (let index = 0; index < listings.length; index++) {
        await this.generalLandingPageRepository.save({
          id: listings[index].id,
          sort_order: index + 2,
        })
      }
      return
    }

    for (let index = 0; index < listings.length; index++) {
      await this.generalLandingPageRepository.save({
        id: listings[index].id,
        sort_order: index + 1,
      })
    }
  }

  private validateSection(section?: string) {
    if (section && !SECTION_VALUES.includes(section)) {
      return validationMessage(messageKey.invalid, {
        ":field": "section",
      })
    }
    return null
  }

  private validateNextScreen(nextScreen?: string) {
    if (nextScreen && !NEXT_SCREEN_VALUES.includes(nextScreen)) {
      return validationMessage(messageKey.invalid, {
        ":field": "next_screen",
      })
    }
    return null
  }

  async create(createDto: CreateGeneralLandingPageDto) {
    try {
      if (isEmpty(createDto.title)) {
        return failureResponse(
          code.SUCCESS,
          validationMessage(messageKey.field_required, { ":field": "title" }),
        )
      }

      if (isEmpty(createDto.description)) {
        return failureResponse(
          code.SUCCESS,
          validationMessage(messageKey.field_required, {
            ":field": "description",
          }),
        )
      }

      if (isEmpty(createDto.section)) {
        return failureResponse(
          code.SUCCESS,
          validationMessage(messageKey.field_required, { ":field": "section" }),
        )
      }

      const sectionError = this.validateSection(createDto.section)
      if (sectionError) {
        return failureResponse(code.SUCCESS, sectionError)
      }

      if (createDto.section === SECTION.HEADER) {
        const existingHeader =
          await this.generalLandingPageRepository.getByParams({
            where: { section: SECTION.HEADER },
            findOne: true,
          })

        if (existingHeader) {
          return failureResponse(
            code.SUCCESS,
            validationMessage(messageKey.already_exist, {
              ":data": "Header",
            }),
          )
        }
      }

      if (
        createDto.section === SECTION.LISTING &&
        isEmpty(createDto.next_screen)
      ) {
        return failureResponse(
          code.SUCCESS,
          validationMessage(messageKey.field_required, {
            ":field": "next_screen",
          }),
        )
      }

      const nextScreenError = this.validateNextScreen(createDto.next_screen)
      if (nextScreenError) {
        return failureResponse(code.SUCCESS, nextScreenError)
      }

      let sortOrder = 1
      if (createDto.section === SECTION.HEADER) {
        await this.shiftSortOrdersUp()
      } else {
        sortOrder = await this.getNextSortOrder()
      }

      await this.generalLandingPageRepository.save({
        section: createDto.section,
        title: createDto.title,
        description: createDto.description,
        next_screen: createDto.next_screen || null,
        sort_order: sortOrder,
      })

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_add, {
          ":data": "General Landing Page",
        }),
      )
    } catch (error) {
      return failureResponse(
        code.ERROR,
        errorMessage(messageKey.validation_error, {
          ":data": "General Landing Page",
        }),
      )
    }
  }

  async findAll(filterDto: GeneralLandingPageFilterDto) {
    try {
      const { take, skip, search } = filterDto

      const params: any = {
        orderBy: { sort_order: "ASC" },
      }

      if (!isEmpty(take)) {
        params.take = take
        params.skip = skip
      }

      if (!isEmpty(search)) {
        params["search"] = {
          title: search,
          description: search,
          section: search,
          next_screen: search,
        }
      }

      const result: any =
        await this.generalLandingPageRepository.getByParams(params)

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_retrieve, {
          ":data": "General Landing Page",
        }),
        result,
      )
    } catch (error) {
      return failureResponse(
        code.ERROR,
        errorMessage(messageKey.validation_error, {
          ":data": "General Landing Page",
        }),
      )
    }
  }

  async findOne(id: number) {
    try {
      const record: any = await this.generalLandingPageRepository.getByParams({
        where: { id },
        findOne: true,
      })

      if (!record) {
        return failureResponse(
          code.SUCCESS,
          errorMessage(messageKey.data_not_found, {
            ":data": "General Landing Page",
          }),
        )
      }

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.detail_found, {
          ":data": "General Landing Page",
        }),
        record,
      )
    } catch (error) {
      return failureResponse(
        code.ERROR,
        errorMessage(messageKey.validation_error, {
          ":data": "General Landing Page",
        }),
      )
    }
  }

  async update(id: number, updateDto: UpdateGeneralLandingPageDto) {
    try {
      const record = await this.generalLandingPageRepository.getByParams({
        where: { id },
        findOne: true,
      })

      if (!record) {
        return failureResponse(
          code.SUCCESS,
          errorMessage(messageKey.data_not_found, {
            ":data": "General Landing Page",
          }),
        )
      }

      if (updateDto.next_screen) {
        const nextScreenError = this.validateNextScreen(updateDto.next_screen)
        if (nextScreenError) {
          return failureResponse(code.SUCCESS, nextScreenError)
        }
      }

      const currentRecord: any = record
      const section = currentRecord.section

      if (
        section === SECTION.LISTING &&
        isEmpty(updateDto.next_screen) &&
        updateDto.next_screen !== undefined
      ) {
        return failureResponse(
          code.SUCCESS,
          validationMessage(messageKey.field_required, {
            ":field": "next_screen",
          }),
        )
      }

      const payload: any = { id, ...updateDto }
      delete payload.sort_order
      delete payload.section

      if (updateDto.next_screen === null || updateDto.next_screen === "") {
        payload.next_screen = null
      }

      await this.generalLandingPageRepository.save(payload)

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_update, {
          ":data": "General Landing Page",
        }),
      )
    } catch (error) {
      return failureResponse(
        code.ERROR,
        errorMessage(messageKey.validation_error, {
          ":data": "General Landing Page",
        }),
      )
    }
  }

  async remove(id: number) {
    try {
      const record = await this.generalLandingPageRepository.getByParams({
        where: { id },
        findOne: true,
      })

      if (!record) {
        return failureResponse(
          code.SUCCESS,
          errorMessage(messageKey.data_not_found, {
            ":data": "General Landing Page",
          }),
        )
      }

      await this.generalLandingPageRepository.remove({ id }, undefined, false)
      await this.resequenceSortOrder()

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_removed, {
          ":data": "General Landing Page",
        }),
      )
    } catch (error) {
      return failureResponse(
        code.ERROR,
        errorMessage(messageKey.validation_error, {
          ":data": "General Landing Page",
        }),
      )
    }
  }

  async reorder(dto: ReorderGeneralLandingPageDto[]) {
    try {
      if (!Array.isArray(dto) || !dto.length) {
        return failureResponse(
          code.SUCCESS,
          validationMessage(messageKey.field_required, {
            ":field": "sort_order",
          }),
        )
      }

      for (const item of dto) {
        const record: any = await this.generalLandingPageRepository.getByParams(
          {
            where: { id: item.id },
            findOne: true,
          },
        )

        if (record) {
          const sortOrder =
            record.section === SECTION.HEADER ? 1 : Number(item.sort_order)

          await this.generalLandingPageRepository.save({
            id: item.id,
            sort_order: sortOrder,
          })
        }
      }

      await this.resequenceSortOrder()

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_update, { ":data": "Sort Order" }),
      )
    } catch (error) {
      return failureResponse(
        code.ERROR,
        errorMessage(messageKey.validation_error, {
          ":data": "General Landing Page",
        }),
      )
    }
  }
}
