import {
  CallHandler,
  ExecutionContext,
  Injectable,
  NestInterceptor,
} from "@nestjs/common"
import { catchError, Observable, tap } from "rxjs"
import { ApiLogService } from "./v1/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,
  sendErrorSlackNotification,
  validationMessage,
} from "../../utils/helpers"
import { messageKey } from "../../constants/message-keys"

import process from "node:process"

@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 == 422 ? 422 : 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)

        // Only send a response if headers haven't been sent yet
        if (!response.headersSent) {
          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 {
            if (getCurrentEnvironment() === "local") {
              console.log("error================", error)
            }

            getCurrentEnvironment() !== "local" &&
              sendErrorSlackNotification(process.env.ERROR_DEV_LOGS, error)
            response
              .status(500)
              .json(
                failureResponse(code.ERROR, errorMessage(messageKey.exception)),
              )
          }
        }
      }),
    )
  }
}
