/* (C) 2021 YiRing, Inc. */
package com.yiring.common.core;

import cn.hutool.core.convert.Convert;
import cn.hutool.extra.spring.SpringUtil;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serial;
import java.io.Serializable;
import java.util.Objects;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.FieldDefaults;
import org.jetbrains.annotations.PropertyKey;

/**
 * 标准的响应对象（所有的接口响应内容格式都应该是一致的）
 *
 * @author ifzm
 * @version 1.1
 * 2018/9/4 11:05
 */

@SuppressWarnings({ "unchecked", "unused" })
@JsonInclude(JsonInclude.Include.NON_NULL)
@Data
@Builder
@FieldDefaults(level = AccessLevel.PRIVATE)
public class Result<T> implements Serializable {

    @Serial
    private static final long serialVersionUID = -4802543396830024571L;

    /**
     * 注入 I18n
     */
    protected static final I18n I18N;

    static {
        I18N = SpringUtil.getBean(I18n.class);
    }

    /**
     * 接口响应时间
     */
    @Schema(description = "响应时间", example = "2021-01-01 00:00:00")
    String timestamp;

    /**
     * 接口耗时（单位：秒）通常在调试阶段出现
     */
    @Schema(description = "耗时", example = "0.001s")
    String times;

    /**
     * 响应状态码
     */
    @Schema(description = "状态码", example = "200", defaultValue = "200")
    Integer status;

    /**
     * 响应消息
     */
    @Schema(description = "消息", example = "OK", defaultValue = "OK")
    String message;

    /**
     * 业务标识码
     */
    @Schema(description = "业务标识码", example = "0", nullable = true)
    Integer code;

    /**
     * 详细信息，通常为参数校验结果或自定义消息
     */
    @Schema(description = "详细信息", nullable = true)
    String details;

    /**
     * 异常信息，通常在出现服务器错误时会出现该异常
     */
    @Schema(description = "异常信息", nullable = true)
    String error;

    /**
     * 响应内容
     */
    @Schema(description = "内容")
    T body;

    /**
     * 返回成功响应内容（默认）
     *
     * @return Result
     * @see com.yiring.common.core.Status
     */
    public static <T> Result<T> ok() {
        return (Result<T>) Result.builder().status(Status.OK.value()).message(t(Status.OK.getReasonPhrase())).build();
    }

    /**
     * 返回成功响应内容
     *
     * @param body {@link String} {@link I18n}
     * @return Result
     * @see com.yiring.common.core.Status
     */
    public static <T> Result<T> ok(@PropertyKey(resourceBundle = I18n.RESOURCE_BUNDLE) String body) {
        return (Result<T>) Result
            .builder()
            .status(Status.OK.value())
            .message(t(Status.OK.getReasonPhrase()))
            .body(t(body))
            .build();
    }

    /**
     * 返回自定义body的成功响应内容
     *
     * @param body {@link Object}
     * @return Result
     */
    public static <T> Result<T> ok(T body) {
        return (Result<T>) Result
            .builder()
            .status(Status.OK.value())
            .message(t(Status.OK.getReasonPhrase()))
            .body(body)
            .build();
    }

    /**
     * 返回默认的 400 错误响应
     *
     * @return Result
     * @see Status#BAD_REQUEST
     */
    public static <T> Result<T> no() {
        return no(Status.BAD_REQUEST);
    }

    /**
     * 返回默认的 400 错误响应
     *
     * @return Result
     * @see Status#BAD_REQUEST
     */
    public static <T> Result<T> no(@PropertyKey(resourceBundle = I18n.RESOURCE_BUNDLE) String details) {
        return no(Status.BAD_REQUEST, details);
    }

    /**
     * 返回失败响应内容
     *
     * @return Result
     * @see Status#BAD_REQUEST
     */
    public static <T> Result<T> no(Status status) {
        return no(status, null, null, null);
    }

    /**
     * 返回失败响应内容
     *
     * @return Result
     * @see Status
     */
    public static <T> Result<T> no(Status status, @PropertyKey(resourceBundle = I18n.RESOURCE_BUNDLE) String details) {
        return no(status, null, details, null);
    }

    /**
     * 返回失败响应内容
     *
     * @return Result
     * @see Status
     */
    public static <T> Result<T> no(Status status, Throwable error) {
        return no(status, null, null, error);
    }

    /**
     * 返回失败响应内容
     *
     * @return Result
     * @see Status
     */
    public static <T> Result<T> no(
        Status status,
        Integer code,
        @PropertyKey(resourceBundle = I18n.RESOURCE_BUNDLE) String details,
        Throwable error
    ) {
        if (Objects.isNull(code) && Objects.nonNull(details)) {
            String prefix = "Code.";
            if (details.startsWith(prefix)) {
                String codeText = details.replace(prefix, "");
                code = Convert.toInt(codeText);
            } else {
                code = -1;
            }
        }

        Result<T> result = (Result<T>) Result
            .builder()
            .status(status.value())
            .message(t(status.getReasonPhrase()))
            .code(code)
            .details(t(details))
            .build();

        if (error != null) {
            result.setError(error.getMessage());
        }

        return result;
    }

    /**
     * i18n 默认值获取简单包装
     *
     * @param message 文本消息
     * @return i18n 翻译结果文本消息
     */
    public static String t(String message) {
        if (message == null) {
            return null;
        }

        return I18N.get(message, message);
    }
}
