import {
  BadRequestException,
  CallHandler,
  ExecutionContext,
  Injectable,
  InternalServerErrorException,
  NestInterceptor,
  NotFoundException,
  UnauthorizedException,
  UnprocessableEntityException,
} from "@nestjs/common";
import { catchError, Observable, throwError } from "rxjs";
import { map } from 'rxjs/operators';
import { LoggerService } from "src/common/logger/logger.service";
const pjson = require('../../package.json');

@Injectable()
export class ExceptionInterceptor implements NestInterceptor {
  constructor(
    private readonly logger: LoggerService
  ) {}

  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const isProduction = process.env.NODE_ENV === "production";
    return next.handle().pipe(
      map(data => {
        this.logger.responseOut(data)
        return data;
      }),

      catchError((err) => {
        console.error(err)
        this.logger.error(err.toString())

        if (err instanceof NotFoundException) {
          this.logger.responseOut({
            error: true,
            statusCode: 404,
            message: err.message
          })
          return throwError(() => {
            const errorPayload: any = {
              error: {
                code: 404,
                title: "Not found",
                message: err.message
              },
              data: {},
              meta: {
                version: pjson['version']
              }
            };
            return new NotFoundException(errorPayload);
          });
        }
        if (err.constructor.name === "ValidationError") {
          this.logger.responseOut({
            error: true,
            statusCode: 422,
            message: err.message
          })
          // sequelize
          return throwError(() => {
            const errorPayload: any = {
              error: {
                code: 422,
                title: "ValidationError",
                message: err.message
              },
              data: {},
              meta: {
                version: pjson['version']
              }
            };
            return new UnprocessableEntityException(errorPayload);
          });
        }
        if (err instanceof BadRequestException) {
          this.logger.responseOut(err.getResponse())
          // class-validator
          return throwError(() => {
            const errorResponse = (err.getResponse() as any);
            var code = (err.getResponse() as any).code ?? 400;
            var title = (err.getResponse() as any).title ?? "Bad Request";
            const { data, ...customError } = errorResponse
            delete customError.statusCode;
            delete customError.error;

            if (Array.isArray(customError.message)) {
              customError.message = customError.message.join(", ")
            }

            const errorPayload: any = {
              error: {
                code,
                title,
                ...customError
              },
              data: data ?? {},
              meta: {
                version: pjson['version']
              }
            };
            return new BadRequestException(errorPayload);
          });
        }
        if (err instanceof UnauthorizedException) {
          return throwError(() => {
            const errorPayload: any = {
              error: {
                code: 401,
                title: "Unauthorized",
                message: (err.getResponse() as any).message,
              },
              data: {},
              meta: {
                version: pjson['version']
              }
            };
            return new UnauthorizedException(errorPayload);
          });
        }
        this.logger.responseOut({
          error: true,
          statusCode: err?.original?.code ?? 500,
          message: err.message
        })
        return throwError(() => {
          const errorPayload: any = {
            error: {
              code: err?.original?.code,
              title: "Server error",
              message: "Server error",
            },
            data: {},
            meta: {
              version: pjson['version']
            }
          };
          if (
            err.constructor.name == "UniqueConstraintError" &&
            err?.original?.code == "23505"
          ) {
            errorPayload.error.message = err.errors[0].message;
          }
          return new InternalServerErrorException(errorPayload);
        });
      })
    );
  }
}
