package me.qwq.doghouse.pojo.dto;

import java.io.Serializable;

import org.springframework.http.HttpStatus;

import lombok.AccessLevel;
import lombok.Data;
import lombok.Getter;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import me.qwq.doghouse.exception.RException;

/**
 * application/json 返回
 * <p>此处返回若非提前指定，虽然 resultCode 可以设置对应 HttpStatus，
 * 但 Response 的 HttpStatus 依然会是 200.<br>
 * 需要同时指定 HttpStatus 的，见 {@code RException}</p>
 * @see me.qwq.doghouse.exception.RException
 */
@Data
@Accessors(chain=true)
@Slf4j
public class R<T> implements Serializable {

    private static final long serialVersionUID = -7658884488696622015L;

    @Getter(AccessLevel.PRIVATE)
    private int resultCode;
    private String message;
    private T data;

    public R() {
    }

    public R(int resultCode, String message) {
        this.resultCode = resultCode;
        this.message = message;
    }

    /**
     * 是否成功
     * 
     * @return
     */
    public boolean isOk() {
        return HttpStatus.OK.value() == getResultCode();
    }

    /**
     * 对当前 R&lt;T> 类型的 T 数据调用 toString()，并将其结果作为生成的 R&lt;String> 的 data， 返回一个相应的
     * R&lt;String> 对象
     * 
     * @return
     */
    public R<String> toStringifyR() {
        return new R<String>().setResultCode(resultCode).setMessage(message).setData(data.toString());
    }

    /**
     * 返回指定状态且 data 为空(类型为 String)的 R
     * 
     * @param constants
     * @return
     */
    public static R<String> status(HttpStatus constants) {
        return new R<String>().setResultCode(constants.value()).setMessage(constants.getReasonPhrase());
    }

    /**
     * 返回指定状态和 data 的 R
     * 
     * @param constants
     * @return
     */
    public static <T> R<T> status(HttpStatus constants, T data) {
        return new R<T>().setResultCode(constants.value()).setMessage(constants.getReasonPhrase()).setData(data);
    }

    /**
     * 返回 HttpStatus.OK 状态和指定的 Data
     * 
     * @param <T>
     * @param data
     * @return
     */
    public static <T> R<T> ok(T data) {
        return status(HttpStatus.OK, data);
    }

    /**
     * 返回 HttpStatus.OK
     * 
     * @return
     */
    public static R<?> ok() {
        return new R<>().setResultCode(HttpStatus.OK.value());
    }

    /**
     * 返回 HttpStatus.UNAUTHORIZED 状态和指定的 Data
     * 
     * @param <T>
     * @param data
     * @return
     */
    public static <T> R<T> unauthorized(T data) {
        return status(HttpStatus.UNAUTHORIZED, data);
    }

    /**
     * 返回 HttpStatus.UNAUTHORIZED
     * 
     * @return
     */
    public static R<?> unauthorized() {
        return status(HttpStatus.UNAUTHORIZED);
    }

    /**
     * 返回 HttpStatus.BAD_REQUEST 状态和指定的 Data
     * 
     * @param <T>
     * @param data
     * @return
     */
    public static <T> R<T> badRequest(T data) {
        return status(HttpStatus.BAD_REQUEST, data);
    }

    /**
     * 返回 HttpStatus.BAD_REQUEST
     * 
     * @return
     */
    public static R<?> badRequest() {
        return status(HttpStatus.BAD_REQUEST);
    }

    /**
     * 返回 HttpStatus.INTERNAL_SERVER_ERROR 状态和指定的 Data
     * 
     * @param <T>
     * @param data
     * @return
     */
    public static <T> R<T> internalServerError(T data) {
        return status(HttpStatus.INTERNAL_SERVER_ERROR, data);
    }

    /**
     * 返回 HttpStatus.INTERNAL_SERVER_ERROR
     * 
     * @return
     */
    public static R<?> internalServerError() {
        return status(HttpStatus.INTERNAL_SERVER_ERROR);
    }
    
    /**
     * 根据布尔值判断返回<br>
     * true 返回 R.ok()<br>
     * false throw RException.badRequest()
     * @param condition
     * @return
     */
    public static R<?> judge(boolean condition) {
        if (condition) return R.ok();
        throw RException.badRequest();
    }
    
    /**
     * 根据 obj 是否为空返回对应内容<p>
     * obj 不为空时，等效于 <code>R.<i>ok</i>(obj)</code><br>
     * obj 为空时抛出 <code>RException.<i>badRequest</i>()</code>
     * @param obj
     * @return
     */
    public static R<?> judgeNonNull(Object obj) {
        if (obj != null) return R.ok(obj);
        throw RException.badRequest();
    }
    

    /**
     * 根据 obj 是否为空返回对应内容<p>
     * obj 不为空时，等效于 <code>R.<i>ok</i>(obj)</code><br>
     * obj 为空时抛出 <code>RException.<i>badRequest</i>(failedMessage)</code>
     * @param obj
     * @param failedMessage
     * @return
     */
    public static R<?> judgeNonNull(Object obj, String failedMessage) {
        if (obj != null) return R.ok(obj);
        throw RException.badRequest(failedMessage);
    }

    public static R<?> judge(ThrowingSupplier<?> supplier) {
        try {
            return R.ok(supplier.get());
        }
        catch (Exception e) {
            throw RException.badRequest(e.getMessage());
        }
    }
    
    /**
     * 提供一返回值为 boolean 的 supplier，如果 true 则返回 R.ok(), false 则抛出 RException.badRequest(defaltMessage),
     * 抛出错误则抛出 RException.badRequest(e.getMessage())
     * @param supplier
     * @param defaultMessage
     * @return
     */
    public static R<?> judge(ThrowingSupplier<Boolean> supplier, String defaultMessage) {
        try {
            return R.judge(supplier.get(), defaultMessage);
        }
        catch (Exception e) {
            throw RException.badRequest(e.getMessage());
        }
    }
    

    public static R<?> judgeThrow(ThrowingSupplier<?> supplier) throws Exception {
        return R.ok(supplier.get());
    }
    
    /**
     * 根据布尔值判断返回<br>
     * true 返回 R.ok(T okData)<br>
     * false throw RException.badRequest(failedMessage)
     * @param condition
     * @return
     */
    public static <T> R<?> judge(boolean condition, T okData, String failedMessage) {
        if (condition) return R.ok(okData);
        throw RException.badRequest(failedMessage);
    }

    /**
     * 根据布尔值判断返回<br>
     * true 返回 R.ok(null)<br>
     * false throw RException.badRequest(failedMessage)
     * @param condition
     * @return
     */
    public static R<?> judge(boolean condition, String failedMessage) {
        return R.judge(condition, null, failedMessage);
    }
    
    @FunctionalInterface
    public static interface ThrowingSupplier<T> {
        T get() throws Exception;
    }
}
