import { logger } from "@/core/logger";
import { NextFunction, Request, Response } from "express";
import httpStatus from "http-status";
import { ApiError } from "./api-error";

export abstract class ExpressController {
  protected req!: Request; //| AuthenticatedRequest | TabContextRequest;
  protected res!: Response;
  protected next!: NextFunction;

  static errorResponse(apiError: ApiError, res: Response) {
    logger.error(`[${apiError.status}] ${apiError.title}: ${apiError.detail}`);
    return res.status(apiError.status).json(apiError);
  }

  protected abstract executeImpl(): Promise<unknown>;

  protected ok<T>(dto?: T) {
    return dto ? this.res.status(httpStatus.OK).json(dto) : this.res.status(httpStatus.OK).send();
  }

  protected created<T>(dto?: T) {
    return dto
      ? this.res.status(httpStatus.CREATED).json(dto)
      : this.res.status(httpStatus.CREATED).send();
  }

  protected noContent() {
    return this.res.status(httpStatus.NO_CONTENT).send();
  }

  /**
   * 🔹 Respuesta para errores de cliente (400 Bad Request)
   */
  public clientError(message: string, errors?: any[] | any) {
    return ExpressController.errorResponse(
      new ApiError({
        status: 400,
        title: "Bad Request",
        detail: message,
        errors: Array.isArray(errors) ? errors : [errors],
      }),
      this.res
    );
  }

  /**
   * 🔹 Respuesta para errores de autenticación (401 Unauthorized)
   */
  protected unauthorizedError(message?: string) {
    return ExpressController.errorResponse(
      new ApiError({
        status: 401,
        title: httpStatus["401"],
        name: httpStatus["401_NAME"],
        detail: message ?? httpStatus["401_MESSAGE"],
      }),
      this.res
    );
  }

  /**
   * 🔹 Respuesta para errores de autorización (403 Forbidden)
   */
  protected forbiddenError(message?: string) {
    return ExpressController.errorResponse(
      new ApiError({
        status: 403,
        title: "Forbidden",
        detail: message ?? "You do not have permission to perform this action.",
      }),
      this.res
    );
  }

  /**
   * 🔹 Respuesta para recursos no encontrados (404 Not Found)
   */
  protected notFoundError(message: string) {
    return ExpressController.errorResponse(
      new ApiError({
        status: 404,
        title: "Not Found",
        detail: message,
      }),
      this.res
    );
  }

  /**
   * 🔹 Respuesta para conflictos (409 Conflict)
   */
  protected conflictError(message: string, errors?: any[]) {
    return ExpressController.errorResponse(
      new ApiError({
        status: 409,
        title: "Conflict",
        detail: message,
        errors,
      }),
      this.res
    );
  }

  /**
   * 🔹 Respuesta para errores de validación de entrada (422 Unprocessable Entity)
   */
  protected invalidInputError(message: string, errors?: any[]) {
    return ExpressController.errorResponse(
      new ApiError({
        status: 422,
        title: httpStatus["422"],
        name: httpStatus["422_NAME"],
        detail: message ?? httpStatus["422_MESSAGE"],
        errors,
      }),
      this.res
    );
  }

  /**
   * Respuesta para errores de servidor no disponible (503 Service Unavailable)
   * @param message
   * @returns
   */
  protected unavailableError(message?: string) {
    return ExpressController.errorResponse(
      new ApiError({
        status: 503,
        title: httpStatus["503"],
        name: httpStatus["503_NAME"],
        detail: message ?? httpStatus["503_MESSAGE"],
      }),
      this.res
    );
  }

  /**
   * 🔹 Respuesta para errores internos del servidor (500 Internal Server Error)
   */
  protected internalServerError(message?: string) {
    return ExpressController.errorResponse(
      new ApiError({
        status: 500,
        title: httpStatus["500"],
        name: httpStatus["500_NAME"],
        detail: message ?? httpStatus["500_MESSAGE"],
      }),
      this.res
    );
  }

  public execute(req: Request, res: Response, next: NextFunction): void {
    this.req = req;
    this.res = res;
    this.next = next;

    try {
      this.executeImpl();
    } catch (error: unknown) {
      const _error = error as Error;
      logger.error(`Unhandled error in controller: ${_error.message}`);
      this.internalServerError(_error.message);
    }
  }
}
