import {
  CallHandler,
  ExecutionContext,
  Injectable,
  NestInterceptor,
} from "@nestjs/common"
import { catchError, Observable, tap } from "rxjs"
import { ApiLogService } from "./api-log.service"
import { CreateApiLogDto } from "./dto/create-api-log.dto"
import moment from "moment"
import { code } from "../../common/response/response.code"
import { failureResponse } from "../../common/response/response"
import {
  errorMessage,
  getCurrentEnvironment,
  sendEmailNotification,
  validationMessage,
} from "../../utils/helpers"
import { messageKey } from "../../constants/message-keys"
import { exceptionEmail } from "../../common/emails/templates/exception-email"
import { mailSubject } from "../../common/emails/email-subjects"
import { EV } from "../../utils/env.values"

@Injectable()
export class ApiLogInterceptor implements NestInterceptor {
  constructor(private readonly apiLogService: ApiLogService) {}

  intercept(
    context: ExecutionContext,
    next: CallHandler<any>,
  ): Observable<any> | Promise<Observable<any>> {
    const startTime = moment()

    return next.handle().pipe(
      tap((data) => {
        const ctx = context.switchToHttp()
        const request = ctx.getRequest<Request>()
        const method = request.method
        const url = request.url
        const headers = request.headers
        const body = request.body

        const endTime = moment()
        const resTime = endTime.diff(startTime)

        // Create and save log entry
        const logEntry = new CreateApiLogDto()
        logEntry.header = JSON.stringify(headers)
        logEntry.url = url
        logEntry.method = method
        logEntry.response_code = data?.code
        logEntry.response_time = resTime.toString()
        logEntry.form_data = JSON.stringify(body)
        logEntry.response = JSON.stringify(data) // Log the response data

        this.apiLogService.saveLog(logEntry)
      }),
      catchError(async (error: any) => {
        const ctx = context.switchToHttp()
        const request = ctx.getRequest<Request>()
        const response: any = ctx.getResponse()
        const method = request.method
        const url = request.url
        const headers = request.headers
        const body = request.body

        const endTime = moment()
        const resTime = endTime.diff(startTime)

        // Create and save log entry
        const logEntry = new CreateApiLogDto()
        logEntry.header = JSON.stringify(headers)
        logEntry.url = url
        logEntry.method = method
        logEntry.response_code =
          error?.response?.status != 500 ? error?.status : code.ERROR
        logEntry.response_time = resTime.toString()
        logEntry.form_data = JSON.stringify(body)
        logEntry.response =
          error?.response?.status == 422 ? error : JSON.stringify(error.stack)

        await this.apiLogService.saveLog(logEntry)

        if (error?.response) {
          if (error?.response?.status == 422) {
            response.status(code.VALIDATION).json({
              success: false,
              message: validationMessage(messageKey.validation_error),
              status: code.VALIDATION,
              errors: error?.response.errors,
            })
          } else {
            const statusCode = error?.response?.status || 400
            const errorMessageResponse =
              error?.response?.message || "An error occurred"

            response.status(statusCode).json({
              success: false,
              message: errorMessageResponse,
              status: statusCode,
            })
          }
        } else {
          if (getCurrentEnvironment() === "local") {
            console.log("error================", error)
          }

          getCurrentEnvironment() !== "local" &&
            sendEmailNotification(
              EV["DEVELOPER_EMAIL_FOR_EXCEPTION"],
              exceptionEmail(error),
              mailSubject.exceptionMail.replace(":env", EV["NODE_ENV"]),
              EV["DEVELOPER_CC_EMAIL_FOR_EXCEPTION"],
              EV["DEVELOPER_BCC_EMAIL_FOR_EXCEPTION"],
            )

          response
            .status(500)
            .json(
              failureResponse(code.ERROR, errorMessage(messageKey.exception)),
            )
        }
      }),
    )
  }
}
