package com.limap.common.core.domain;


import java.io.Serializable;
import java.util.*;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONWriter;
import com.limap.common.constant.HttpStatus;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

@Data
@Slf4j
public class AjaxResult1<T> implements Serializable {
    private static final long serialVersionUID = -2740653620215944417L;

    /**
     * 状态码
     */
    public static final String CODE_TAG = "code";

    /**
     * 返回内容
     */
    public static final String MSG_TAG = "msg";

    /**
     * 数据对象
     */
    public static final String DATA_TAG = "data";

    /**
     * 状态码，告诉swagger/knife4j返回对象有一个code属性
     */
    private int code;

    /**
     * 返回内容，告诉swagger/knife4j返回对象有一个msg属性
     */

    private String msg;

    /**
     * 数据对象，告诉swagger/knife4j返回对象有一个泛型数据对象属性
     */

    public T data;

    /**
     * 私有化唯一的构造器，是对象创建只能由create进行
     */
    private AjaxResult1() {
    }

    /**
     * 唯一创建对象实例的方法，实际创建的是子类对象AjaxResult1Impl
     *
     * @param code 状态码
     * @param msg  返回提示消息
     * @param data 返回是数据
     * @param <T>  数据类型
     * @return 返回AjaxResult1，但实际类型为AjaxResult1的子类AjaxResult1Impl
     */
    public static <T> AjaxResult1<T> create(int code, String msg, T data) {
        AjaxResult1Impl<T> result = new AjaxResult1Impl<>();
        result.setCode(code);
        result.setMsg(msg);
        result.setData(data);
        return result;
    }

    /**
     * 成功
     *
     * @return r
     */
    public static AjaxResult1<Void> success() {
        return AjaxResult1.successMsg("操作成功");
    }

    /**
     * 返回成功数据
     *
     * @return 成功消息，并携带泛型类型，便于swagger预览返回对象结构
     */
    public static <T> AjaxResult1<T> successRT(T data) {
        return success(data);
    }

    public static <T> AjaxResult1<T> success(T data) {
        return AjaxResult1.success("操作成功", data);
    }

    /**
     * 返回成功消息
     *
     * @param msg 返回内容
     * @return 成功消息
     */
    public static AjaxResult1<Void> successMsg(String msg) {
        return AjaxResult1.success(msg, null);
    }

//    /**
//     * 返回成功消息
//     * todo 这是若依的另一个坑，该方法与success(Object data)方法存在重叠，
//     * 比如想返回一个字符串路径，但期望调用的时success(Object data)方法，
//     * 但实际调用的是success(String msg)方法，导致data的数据放置到msg上面，
//     * 应该有数据的data却是空的。
//     * 此处的解决方案是直接加一个successMsg(String msg)方法，专门用于返回成功消息，
//     * 避免与success(Object data)方法混淆，
//     * 同时修改项目中使用的success(String msg)方法，改用successMsg(String msg)方法
//     * @param msg 返回内容
//     * @return 成功消息
//     */
//    public static AjaxResult1<Void> success(String msg) {
//        return AjaxResult1.success(msg, null);
//    }

    /**
     * 返回成功消息
     *
     * @param msg  返回内容
     * @param data 数据对象
     * @return 成功消息
     */
    public static <T> AjaxResult1<T> success(String msg, T data) {
        return AjaxResult1.create(HttpStatus.SUCCESS, msg, data);
    }

    /**
     * 返回错误消息
     *
     * @return r
     */
    public static AjaxResult1<Void> error() {
        return AjaxResult1.error("操作失败");
    }

    /**
     * 返回错误消息
     *
     * @param msg 返回内容
     * @return 警告消息
     */
    public static AjaxResult1<Void> error(String msg) {
        return AjaxResult1.error(msg, null);
    }

    /**
     * 返回错误消息
     *
     * @param msg  返回内容
     * @param data 数据对象
     * @return 警告消息
     */
    public static <T> AjaxResult1<T> error(String msg, T data) {
        return AjaxResult1.create(HttpStatus.ERROR, msg, data);
    }

    /**
     * 返回错误消息
     *
     * @param code 状态码
     * @param msg  返回内容
     * @return 警告消息
     */
    public static AjaxResult1<Void> error(int code, String msg) {
        return AjaxResult1.create(code, msg, null);
    }

    /**
     * 方便链式调用
     *
     * @param key   键
     * @param value 值
     * @return 数据对象
     */
    public AjaxResult1<T> put(String key, Object value) {
        return this;
    }

    public static class AjaxResult1Impl<T> extends AjaxResult1<T> implements Map<String, Object> {
        private static final long serialVersionUID = -6041138310168295064L;

        /**
         * 定义实际保存数据的容器
         */
        private final Map<String, Object> dataMap;

        /**
         * 创建子类对象时创建数据容器
         */
        public AjaxResult1Impl() {
            dataMap = new HashMap<>();
        }

        /**
         * 重写getCode，从数据容器中获取code
         *
         * @return r
         */
        @Override
        public int getCode() {
            return (Integer) dataMap.getOrDefault(CODE_TAG, 0);
        }

        /**
         * 重写getMsg，从数据容器中获取msg
         *
         * @return r
         */
        @Override
        public String getMsg() {
            return (String) dataMap.get(MSG_TAG);
        }

        /**
         * 重写getData，从数据容器中获取data
         *
         * @return r
         */
        @Override
        @SuppressWarnings("all")
        public T getData() {
            return (T) dataMap.get(data);
        }

        /**
         * 重写setCode，把code放到数据容器中
         */
        @Override
        public void setCode(int code) {
            dataMap.put(CODE_TAG, code);
        }

        /**
         * 重写setCode，把msg放到数据容器中
         */
        @Override
        public void setMsg(String msg) {
            dataMap.put(MSG_TAG, msg);
        }

        /**
         * 重写setData，把data放到数据容器中
         */
        @Override
        public void setData(T data) {
            dataMap.put(DATA_TAG, data);
        }

        /**
         * 重写put，直接使用数据容器的put方法，并返回this
         *
         * @return 返回AjaxResult1类型的AjaxResult1Impl实例对象
         */
        @Override
        public AjaxResult1<T> put(String key, Object value) {
            dataMap.put(key, value);
            return this;
        }

        /**
         * 重写toString方法，直接使用数据容器的toString方法
         *
         * @return r
         */
        @Override
        public String toString() {
            return dataMap.toString();
        }

        //以下方法为Map接口需要实现的方法，直接使用数据容器的对应方法即可
        @Override
        public int size() {
            return dataMap.size();
        }

        @Override
        public boolean isEmpty() {
            return dataMap.isEmpty();
        }

        @Override
        public boolean containsKey(Object key) {
            return dataMap.containsKey(key);
        }

        @Override
        public boolean containsValue(Object value) {
            return dataMap.containsValue(value);
        }

        @Override
        public Object get(Object key) {
            return dataMap.get(key);
        }

        @Override
        public Object remove(Object key) {
            return dataMap.remove(key);
        }

        @Override
        public void putAll(Map<? extends String, ?> m) {
            dataMap.putAll(m);
        }

        @Override
        public void clear() {
            dataMap.clear();
        }

        @Override
        public Set<String> keySet() {
            return dataMap.keySet();
        }

        @Override
        public Collection<Object> values() {
            return dataMap.values();
        }

        @Override
        public Set<Entry<String, Object>> entrySet() {
            return dataMap.entrySet();
        }
    }

    /**
     * 测试代码
     *
     * @param args a
     */
    public static void main(String[] args) {
        AjaxResult1<String> builder = AjaxResult1.success("返回数据");
        AjaxResult1<AjaxResult1<String>> success = AjaxResult1.success(builder);
        System.out.println(JSON.toJSONString(builder, JSONWriter.Feature.PrettyFormat));
        builder.put("a1", "1");
        builder.put("a2", "2");
        builder.put("a3", "3");
        builder.put("a4", "4");
        builder.put("1", "4");
        builder.put("a4", "4");
        System.out.println(JSON.toJSONString(builder, JSONWriter.Feature.PrettyFormat));
        System.out.println(builder);
        System.out.println();
        success.put("测试1", "test1")
                .put("test2", "test2")
                .put("test3", "test3");
        System.out.println(JSON.toJSONString(success, JSONWriter.Feature.PrettyFormat));
        Object code1 = ((AjaxResult1Impl<?>) success).remove("code");
        System.out.println(code1);
        System.out.println(success.getCode());
    }

}


