package com.kamco.cd.training.config.api;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.kamco.cd.training.common.utils.enums.EnumType;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import org.springframework.http.HttpStatus;

@Getter
@ToString
public class ApiResponseDto<T> {

  private T data;

  @JsonInclude(JsonInclude.Include.NON_NULL)
  private Error error;

  @JsonInclude(JsonInclude.Include.NON_NULL)
  private T errorData;

  @JsonIgnore private HttpStatus httpStatus;

  @JsonIgnore private Long errorLogUid;

  public ApiResponseDto(T data) {
    this.data = data;
  }

  private ApiResponseDto(T data, HttpStatus httpStatus) {
    this.data = data;
    this.httpStatus = httpStatus;
  }

  public ApiResponseDto(ApiResponseCode code) {
    this.error = new Error(code.getId(), code.getMessage());
  }

  public ApiResponseDto(ApiResponseCode code, String message) {
    this.error = new Error(code.getId(), message);
  }

  public ApiResponseDto(ApiResponseCode code, String message, HttpStatus httpStatus) {
    this.error = new Error(code.getId(), message);
    this.httpStatus = httpStatus;
  }

  public ApiResponseDto(
      ApiResponseCode code, String message, HttpStatus httpStatus, Long errorLogUid) {
    this.error = new Error(code.getId(), message);
    this.httpStatus = httpStatus;
    this.errorLogUid = errorLogUid;
  }

  public ApiResponseDto(ApiResponseCode code, String message, T errorData) {
    this.error = new Error(code.getId(), message);
    this.errorData = errorData;
  }

  // HTTP 상태 코드가 내장된 ApiResponseDto 반환 메서드들
  public static <T> ApiResponseDto<T> createOK(T data) {
    return new ApiResponseDto<>(data, HttpStatus.CREATED);
  }

  public static <T> ApiResponseDto<T> ok(T data) {
    return new ApiResponseDto<>(data, HttpStatus.OK);
  }

  public static <T> ApiResponseDto<ResponseObj> okObject(ResponseObj data) {
    if (data.getCode().equals(ApiResponseCode.OK)) {
      return new ApiResponseDto<>(data, HttpStatus.NO_CONTENT);
    } else {
      return new ApiResponseDto<>(data.getCode(), data.getMessage(), HttpStatus.CONFLICT);
    }
  }

  public static <T> ApiResponseDto<T> deleteOk(T data) {
    return new ApiResponseDto<>(data, HttpStatus.NO_CONTENT);
  }

  public static ApiResponseDto<String> createException(ApiResponseCode code) {
    return new ApiResponseDto<>(code);
  }

  public static ApiResponseDto<String> createException(ApiResponseCode code, String message) {
    return new ApiResponseDto<>(code, message);
  }

  public static ApiResponseDto<String> createException(
      ApiResponseCode code, String message, HttpStatus httpStatus) {
    return new ApiResponseDto<>(code, message, httpStatus);
  }

  public static ApiResponseDto<String> createException(
      ApiResponseCode code, String message, HttpStatus httpStatus, Long errorLogUid) {
    return new ApiResponseDto<>(code, message, httpStatus, errorLogUid);
  }

  public static <T> ApiResponseDto<T> createException(
      ApiResponseCode code, String message, T data) {
    return new ApiResponseDto<>(code, message, data);
  }

  @Getter
  public static class Error {

    private final String code;
    private final String message;

    public Error(String code, String message) {
      this.code = code;
      this.message = message;
    }
  }

  /** Error가 아닌 Business상 성공이거나 실패인 경우, 메세지 함께 전달하기 위한 object */
  @Getter
  public static class ResponseObj {

    private final ApiResponseCode code;
    private final String message;

    public ResponseObj(ApiResponseCode code, String message) {
      this.code = code;
      this.message = message;
    }
  }

  @Getter
  @RequiredArgsConstructor
  public enum ApiResponseCode implements EnumType {

    // @formatter:off
    OK("요청이 성공하였습니다."),
    BAD_REQUEST("요청 파라미터가 잘못되었습니다."),
    BAD_GATEWAY("네트워크 상태가 불안정합니다."),
    ALREADY_EXIST_MALL("이미 등록된 쇼핑센터입니다."),
    NOT_FOUND_MAP("지도를 찾을 수 없습니다."),
    UNAUTHORIZED("권한이 없습니다."),
    CONFLICT("이미 등록된 컨텐츠입니다."),
    NOT_FOUND("Resource를 찾을 수 없습니다."),
    NOT_FOUND_DATA("데이터를 찾을 수 없습니다."),
    NOT_FOUND_WEATHER_DATA("날씨 데이터를 찾을 수 없습니다."),
    FAIL_SEND_MESSAGE("메시지를 전송하지 못했습니다."),
    TOO_MANY_CONNECTED_MACHINES("연결된 기기가 너무 많습니다."),
    UNAUTHENTICATED("인증에 실패하였습니다."),
    INVALID_TOKEN("잘못된 토큰입니다."),
    EXPIRED_TOKEN("만료된 토큰입니다."),
    INTERNAL_SERVER_ERROR("서버에 문제가 발생 하였습니다."),
    FORBIDDEN("권한을 확인해주세요."),
    INVALID_PASSWORD("잘못된 비밀번호 입니다."),
    NOT_FOUND_CAR_IN("입차정보가 없습니다."),
    WRONG_STATUS("잘못된 상태입니다."),
    FAIL_VERIFICATION("인증에 실패하였습니다."),
    INVALID_EMAIL("잘못된 형식의 이메일입니다."),
    REQUIRED_EMAIL("이메일은 필수 항목입니다."),
    WRONG_PASSWORD("잘못된 패스워드입니다."),
    DUPLICATE_EMAIL("이미 가입된 이메일입니다."),
    DUPLICATE_DATA("이미 등록되어 있습니다."),
    DATA_INTEGRITY_ERROR("데이터 무결성이 위반되어 요청을 처리할수 없습니다."),
    FOREIGN_KEY_ERROR("참조 중인 데이터가 있어 삭제할 수 없습니다."),
    DUPLICATE_EMPLOYEEID("이미 가입된 사번입니다."),
    NOT_FOUND_USER_FOR_EMAIL("이메일로 유저를 찾을 수 없습니다."),
    NOT_FOUND_USER("사용자를 찾을 수 없습니다."),
    UNPROCESSABLE_ENTITY("이 데이터는 삭제할 수 없습니다."),
    UNPROCESSABLE_ENTITY_UPDATE("이 데이터는 수정할 수 없습니다."),
    LOGIN_ID_NOT_FOUND("아이디를 잘못 입력하셨습니다."),
    LOGIN_PASSWORD_MISMATCH("비밀번호를 잘못 입력하셨습니다."),
    LOGIN_PASSWORD_EXCEEDED("비밀번호 오류 횟수를 초과하여 이용하실 수 없습니다.\n로그인 오류에 대해 관리자에게 문의하시기 바랍니다."),
    REFRESH_TOKEN_EXPIRED_OR_REVOKED("토큰 정보가 만료 되었습니다."),
    REFRESH_TOKEN_MISMATCH("토큰 정보가 일치하지 않습니다."),
    INACTIVE_ID("사용할 수 없는 계정입니다."),
    INVALID_EMAIL_TOKEN(
        "You can only reset your password within 24 hours from when the email was sent.\n"
            + "To reset your password again, please submit a new request through \"Forgot"
            + " Password.\""),
    ;
    // @formatter:on
    private final String message;

    @Override
    public String getId() {
      return name();
    }

    @Override
    public String getText() {
      return message;
    }

    public static ApiResponseCode getCode(String name) {
      if (name == null || name.isBlank()) return null;
      try {
        return ApiResponseCode.valueOf(name.toUpperCase());
      } catch (IllegalArgumentException ex) {
        return null;
      }
    }

    public static String getMessage(String name) {
      return ApiResponseCode.valueOf(name.toUpperCase()).getText();
    }
  }
}
