package com.sikey.wa04.business.infrastructure.adapter.push;

import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.sikey.wa04.business.infrastructure.adapter.push.template.NotificationMessageTemplate;
import com.sikey.wa04.business.infrastructure.configuration.PushConfig;
import com.sikey.wa04.common.enums.MessageType;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.Singular;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.jetbrains.annotations.NotNull;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 小米推送通知
 *
 * @author yuzenghui
 */
@Setter
@Slf4j
public class XiaomiPushNotification implements AndroidPushNotification  {
    private static final String AUTHENTICATE_REQUEST_URL = "https://api.xmpush.xiaomi.com/v3/message/regid";
    private static final String AUTHENTICATE_REQUEST_CONTENT_TYPE = "multipart/form-data";
    //App的包名
    private static final String RESTRICTED_PACKAGE_NAME = "com.sikey.interconnect";

    //聊天channel_id，默认除视频通话外都传这个，小米推送运营平台申请
    private static final String Def_CHANNEL_ID = "139546";

    //视频通话专用channel_id，小米推送运营平台申请
    private static final String VIDEO_CALL_IN_CHANNEL_ID = "140735";

    //OKHTTP发送失败的重发次数
    private static final Integer DEFAULT_MAX_RETRY = 3;

    private static final ObjectMapper objectMapper;

    static {
        objectMapper = new Jackson2ObjectMapperBuilder().build();
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);//如果希望 notify_id 在未赋值时不出现，增加此配置，如果希望保留 null 字段，则无需额外配置（默认行为）
        objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);//JSON 属性命名策略为 snake_case（蛇形命名法）,snake_case 是一种命名规范，特点是：所有字母小写 单词之间用下划线 _ 分隔
    }

    private PushConfig pushConfig;

    private volatile OkHttpClient httpClient;

    private OkHttpClient getHttpClient() {
        if (httpClient == null) {
            synchronized (this) {
                if (httpClient == null) {
                    httpClient = new OkHttpClient.Builder()  // 使用 Builder 模式创建 OkHttpClient
                            .connectTimeout(60, TimeUnit.SECONDS)  // 设置连接超时时间
                            .readTimeout(60, TimeUnit.SECONDS)     // 设置读取超时时间
                            .writeTimeout(60, TimeUnit.SECONDS)    // 设置写入超时时间
                            .build();  // 构建 OkHttpClient 实例
                }
            }
        }
        return httpClient;
    }

    /**
     * 获取小米配置信息
     * @param pushConfig 推送配置
     * @return
     */
    @Override
    public AccessToken authenticate(PushConfig pushConfig) {
        setPushConfig(Objects.requireNonNull(pushConfig));
        return new AccessToken().setValue(pushConfig.getXiaomi().getAppSecret());
    }

    /**
     * 发送推送消息到小米平台
     *
     * @param template 消息模板，包含标题、内容、接收者等信息
     * @param accessToken 认证令牌，实际使用小米的AppSecret
     * @return 发送结果，包含是否成功和重试次数
     * @throws IOException 当网络通信或小米平台返回错误时抛出
     * @throws IllegalArgumentException 当参数不合法时抛出
     */
    @Override
    public SendResult send(NotificationMessageTemplate template, AccessToken accessToken) throws IOException {
        log.info("[XiaomiPushNotification]send请求参数: template:{} accessToken: {}", objectMapper.writeValueAsString(template), accessToken.toString());
        //组装消息message
        RequestBody formBody = getXiaomiFormBody(template);
        Request request = new Request.Builder()
                .post(formBody)
                .addHeader("Authorization", String.format("key=%s", accessToken.getValue()))
                .url(AUTHENTICATE_REQUEST_URL)
                .build();

        //请求小米接口推送消息，只要response.isSuccessful()不是成功就重试请求默认次数
        AtomicInteger retryNum = new AtomicInteger();
        final int maxRetry = Optional.ofNullable(pushConfig.getXiaomi().getMaxRetry()).orElse(DEFAULT_MAX_RETRY);
        OkHttpClient httpClient = new OkHttpClient().newBuilder()
                .addInterceptor((chain) -> {
                    Response response = chain.proceed(chain.request());
                    while (!response.isSuccessful() && retryNum.get() < maxRetry) {
                        retryNum.getAndIncrement();
                        response = chain.proceed(chain.request());
                    }
                    return response;
                })
                .build();
        Response response = httpClient.newCall(request).execute();
        if (response.isSuccessful()) {
            String responseBody = response.body() != null ? response.body().string() : "";
            log.info("[XiaomiPushNotification] Send Xiaomi successfully, {}", responseBody);
        }
        return new SendResult()
                .setSuccessful(response.isSuccessful())
                .setRetryNum(retryNum.get());
    }


    /**
     * 组装请求小米推送接口的消息数据
     * @param template
     * @return
     * @throws JsonProcessingException
     */
    @NotNull
    private static RequestBody getXiaomiFormBody(NotificationMessageTemplate template) throws JsonProcessingException {
        String channel_id = Def_CHANNEL_ID;
        String notify_id = String.valueOf(Instant.now().toEpochMilli());
        // 初始化 Builder
        FormBody.Builder builder = new FormBody.Builder(StandardCharsets.UTF_8)
                .add("payload", URLEncoder.encode(objectMapper.writeValueAsString(template.getParams()), StandardCharsets.UTF_8))
                .add("restricted_package_name", RESTRICTED_PACKAGE_NAME)
                .add("registration_id", template.getRegId())
                .add("extra.notify_effect", "1")
                .add("time_to_live", "21600000"); // 6小时

        //当是视频通话的类型时xiaomiMessage结构体中需要添加notify_id字段，并且赋值MessageType.VIDEO_CALL_IN_NOTICE_MESSAGE.getValue()
        if (Objects.equals(template.getMsgType(), MessageType.VIDEO_CALL_IN_NOTICE_MESSAGE.getValue())) {
            notify_id = String.valueOf(MessageType.VIDEO_CALL_IN_NOTICE_MESSAGE.getValue());
            channel_id = VIDEO_CALL_IN_CHANNEL_ID;
        } else if(MessageType.getChatMsgTypes().contains(template.getMsgType())) {
            notify_id = String.valueOf(MessageType.CHAT_TEXT_MESSAGE.getValue());
        } else if (Objects.equals(template.getMsgType(), MessageType.LOCATION_EXIT_SAFETY_FENCE_NOTICE_MESSAGE.getValue())) {
            notify_id = String.valueOf(MessageType.LOCATION_EXIT_SAFETY_FENCE_NOTICE_MESSAGE.getValue());
        } else if (Objects.equals(template.getMsgType(), MessageType.LOCATION_ENTER_DANGER_FENCE_NOTICE_MESSAGE.getValue())) {
            notify_id = String.valueOf(MessageType.LOCATION_ENTER_DANGER_FENCE_NOTICE_MESSAGE.getValue());
        } else if (Objects.equals(template.getMsgType(), MessageType.DEVICE_LOW_BATTERY_NOTICE_MESSAGE.getValue())){
            notify_id = String.valueOf(MessageType.DEVICE_LOW_BATTERY_NOTICE_MESSAGE.getValue());
        } else if (Objects.equals(template.getMsgType(), MessageType.VIDEO_CALL_OUT_NOTICE_MESSAGE.getValue())){
            notify_id = String.valueOf(MessageType.VIDEO_CALL_OUT_NOTICE_MESSAGE.getValue());
        }

        builder.add("title", template.getTitle());
        builder.add("description", template.getBody());
        builder.add("notify_id", notify_id);
        builder.add("extra.channel_id", channel_id);

        // 打印表单内容
//        FormBody formBody = builder.build();
//        for (int i = 0; i < formBody.size(); i++) {
//            log.info("[getXiaomiFormBody]FormBody:{}",formBody.name(i) + ": " + formBody.value(i));
//        }
        return builder.build();
    }

//  只有当请求的格式是application/json; charset=utf-8  才用这套数据做请求
//    @NotNull
//    private static String getXiaomiData(NotificationMessageTemplate template) throws JsonProcessingException {
//        //组装message
//        XiaomiMessage.XiaomiMessageBuilder xiaomiMessage = XiaomiMessage.builder()
//                .payload(objectMapper.writeValueAsString(template.getParams()))
//                .restricted_package_name(RESTRICTED_PACKAGE_NAME)
//                .title(template.getTitle())
//                .description(template.getBody())
//                .alias(template.getRegId())
//                .time_to_live((long) (6*60*60*1000))
//                .extras(Map.of(
//                        "channel_id", Def_CHANNEL_ID,
//                        "notify_effect", "1",
//                ));
//        //当是视频通话的类型时xiaomiMessage结构体中需要添加notify_id字段，并且赋值MessageType.VIDEO_CALL_IN_NOTICE_MESSAGE.getValue()
//        if (Objects.equals(template.getMsgType(), MessageType.VIDEO_CALL_IN_NOTICE_MESSAGE.getValue())) {
//            xiaomiMessage.extras(Map.of(
//                    "channel_id", VIDEO_CALL_IN_CHANNEL_ID,
//                    "notify_effect", "1",
//            ));
//            //添加xiaomiMessage结构体中增加notify_id字段
//            xiaomiMessage.notify_id(MessageType.VIDEO_CALL_IN_NOTICE_MESSAGE.getValue());
//        }
//
//        return objectMapper.writeValueAsString(xiaomiMessage.build());
//    }
//
//    /**
//     * 小米Message消息 POST参数说明
//     *
//     */
//    @Getter
//    @Builder(toBuilder = true)
//    public static class XiaomiMessage {
//        private String payload; //消息的内容。（注意：需要对payload字符串做urlencode处理）
//        private String restricted_package_name; //App的包名。备注：V2版本支持一个包名，V3版本支持多包名（中间用逗号分割）。
//        private String title; //通知栏展示的通知的标题，不允许全是空白字符，长度小于50， 一个中英文字符均计算为1（通知栏消息必填）。
//        private String description;//通知栏展示的通知的描述，不允许全是空白字符，长度小于128，一个中英文字符均计算为1（通知栏消息必填）。
//        private String alias;//据registration_id，发送消息到指定设备上。可以提供多个registration_id，发送给一组设备，不同的registration_id之间用“,”分割。参数仅适用于“/message/regid”HTTP API。
//        private Integer notify_id;
//        private Long time_to_live;
//
//        // 使用Map存储所有extra字段
//        @JsonIgnore
//        private Map<String, String> extras;
//
//        // 特殊注解处理extra字段
//        @JsonAnyGetter
//        public Map<String, String> getExtrasForSerialization() {
//            Map<String, String> result = new HashMap<>();
//            extras.forEach((k, v) -> result.put("extra." + k, v));
//            return result;
//        }
//
//        //如果有时间字段 需要时间字段需要添加注解@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai") 才会得到2023-08-01 12:30:45类型的值
//        //且添加ObjectMapper().disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)禁用此行为，如果不禁用此选项，Jackson 会将日期类型序列化为 时间戳（timestamp） 格式，即一个长整型数字（毫秒数）;
//
//        // 自定义 Builder（仅对需要特殊处理的字段覆盖），如果需要对字段校验、格式化 → 可以在自定义 Builder中添加逻辑，例如：自动对 payload URL 编码。 强制 title 非空。 也可以设置字段默认值(未使用)。
//        public static class XiaomiMessageBuilder {
//            // SimpleDateFormat 是非线程安全的，通过 ThreadLocal 确保每个线程有自己的 DateFormat 实例。
//            //适用场景：如果 XiaomiMessageBuilder 的方法中需要格式化/解析日期，这才有意义。但当前代码中并未使用该字段。
//            private static final ThreadLocal<DateFormat> DATE_FORMAT =
//                    ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
//
//            // 自动编码 payload
//            public XiaomiMessageBuilder payload(String payload) {
//                try {
//                    this.payload = URLEncoder.encode(payload, StandardCharsets.UTF_8);
//                } catch (Exception e) {
//                    throw new RuntimeException("Payload 编码失败", e);
//                }
//                return this;
//            }
//
//            // 校验 title 非空
//            public XiaomiMessageBuilder title(String title) {
//                if (title == null || title.trim().isEmpty()) {
//                    throw new IllegalArgumentException("标题不能为空");
//                }
//                this.title = title;
//                return this;
//            }
//        }
//    }
}
