package com.sino.mci.channel.wecom;

import com.sino.mci.channel.common.dto.CallbackPayload;
import com.sino.mci.channel.common.dto.ChannelInitRequest;
import com.sino.mci.channel.common.dto.ChannelInitResult;
import com.sino.mci.channel.common.dto.SendMessageRequest;
import com.sino.mci.channel.common.dto.SendMessageResult;
import com.sino.mci.channel.common.model.ChannelType;
import com.sino.mci.channel.common.spi.ChannelAdapter;
import com.sino.mci.channel.wecom.support.WeComServiceFactory;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.common.api.WxConsts;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.api.WxCpChatService;
import me.chanjar.weixin.cp.api.WxCpMessageService;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.article.NewArticle;
import me.chanjar.weixin.cp.bean.message.WxCpAppChatMessage;
import me.chanjar.weixin.cp.bean.message.WxCpMessage;
import me.chanjar.weixin.cp.bean.message.WxCpMessageSendResult;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

@Component
@RequiredArgsConstructor
public class WeComChannelAdapter implements ChannelAdapter {

    private static final Logger log = LoggerFactory.getLogger(WeComChannelAdapter.class);

    private final WeComServiceFactory serviceFactory;

    @Override
    public ChannelType getChannelType() {
        return ChannelType.WECOM;
    }

    @Override
    public ChannelInitResult initAccount(ChannelInitRequest request) {
        ChannelInitResult result = new ChannelInitResult();
        Map<String, Object> config = request == null ? null : request.getConfig();
        WxCpService service = serviceFactory.getOrCreateService(config);
        if (service == null) {
            result.setSuccess(false);
            result.setMessage("企微 CP 初始化失败：缺少 corp_id/agent_id/secret");
            return result;
        }

        try {
            // 触发一次 access_token 获取，验证配置可用性
            service.getAccessToken(false);

            String corpId = getString(config, "corp_id");
            String agentId = getString(config, "agent_id");

            result.setSuccess(true);
            result.setAccountId(corpId + ":" + agentId);
            result.setAccountName("WeCom:" + corpId);
            result.setMessage("企微 CP 账号初始化成功");
        } catch (WxErrorException e) {
            result.setSuccess(false);
            result.setMessage("企微 CP 初始化失败：" + e.getMessage());
            log.warn("[WeCom] 初始化账号失败", e);
        } catch (Exception e) {
            result.setSuccess(false);
            result.setMessage("企微 CP 初始化失败：" + e.getMessage());
            log.warn("[WeCom] 初始化账号异常", e);
        }
        return result;
    }

    @Override
    public SendMessageResult sendMessage(SendMessageRequest request) {
        SendMessageResult result = new SendMessageResult();
        if (request == null) {
            result.setSuccess(false);
            result.setMessage("发送请求为空");
            return result;
        }

        WxCpService service = resolveService(request);
        if (service == null) {
            result.setSuccess(false);
            result.setMessage("企微 CP 发送失败：无法确定应用账号（请先初始化或在 extra 中提供 corp_id/agent_id/secret）");
            return result;
        }

        String contentType = StringUtils.defaultString(request.getContentType()).toLowerCase();
        boolean toGroupChat = StringUtils.isNotBlank(request.getConversationId());

        try {
            if (toGroupChat) {
                sendAppChatMessage(service, request, contentType);
            } else {
                WxCpMessageSendResult sendResult = sendAppMessage(service, request, contentType);
                result.setChannelMessageId(sendResult == null ? null : sendResult.getMsgId());
            }
            result.setSuccess(true);
        } catch (WxErrorException e) {
            result.setSuccess(false);
            result.setMessage("企微 CP 发送失败：" + e.getMessage());
            log.warn("[WeCom] 发送消息失败", e);
        } catch (Exception e) {
            result.setSuccess(false);
            result.setMessage("企微 CP 发送失败：" + e.getMessage());
            log.warn("[WeCom] 发送消息异常", e);
        }
        return result;
    }

    @Override
    public CallbackPayload parseCallback(String rawBody) {
        CallbackPayload payload = new CallbackPayload();
        payload.setChannelType(ChannelType.WECOM.name());
        payload.setRawBody(rawBody);
        return payload;
    }

    private WxCpService resolveService(SendMessageRequest request) {
        Map<String, Object> extra = request.getExtra();
        if (extra != null && extra.containsKey("corp_id") && extra.containsKey("agent_id") && extra.containsKey("secret")) {
            return serviceFactory.getOrCreateService(extra);
        }

        Map<String, Object> content = request.getContent();
        if (content != null && content.containsKey("corp_id") && content.containsKey("agent_id") && content.containsKey("secret")) {
            return serviceFactory.getOrCreateService(content);
        }

        // 未显式指定账号时，若缓存中仅有一个账号，则使用该账号
        if (serviceFactory.hasSingleAccount()) {
            return serviceFactory.getSingleAccountService();
        }
        return null;
    }

    private WxCpMessageSendResult sendAppMessage(WxCpService service, SendMessageRequest request,
                                                 String contentType) throws WxErrorException {
        WxCpMessage message = buildAppMessage(request, contentType);
        WxCpMessageService messageService = service.getMessageService();
        return messageService.send(message);
    }

    private void sendAppChatMessage(WxCpService service, SendMessageRequest request,
                                    String contentType) throws WxErrorException {
        WxCpAppChatMessage message = buildAppChatMessage(request, contentType);
        WxCpChatService chatService = service.getChatService();
        chatService.sendMsg(message);
    }

    private WxCpMessage buildAppMessage(SendMessageRequest request, String contentType) {
        Map<String, Object> content = request.getContent();
        String toUser = normalizeUserIds(request.getReceiverId());
        Integer agentId = resolveAgentId(request);

        WxCpMessage message;
        switch (contentType) {
            case "image" -> message = WxCpMessage.IMAGE()
                    .agentId(agentId)
                    .toUser(toUser)
                    .mediaId(getString(content, "media_id"))
                    .build();
            case "file" -> message = WxCpMessage.FILE()
                    .agentId(agentId)
                    .toUser(toUser)
                    .mediaId(getString(content, "media_id"))
                    .build();
            case "markdown" -> message = WxCpMessage.MARKDOWN()
                    .agentId(agentId)
                    .toUser(toUser)
                    .content(getString(content, "text"))
                    .build();
            case "news" -> message = WxCpMessage.NEWS()
                    .agentId(agentId)
                    .toUser(toUser)
                    .articles(buildNewsArticles(content))
                    .build();
            case "miniprogram", "miniprogram_notice" -> message = WxCpMessage.newMiniProgramNoticeBuilder()
                    .agentId(agentId)
                    .toUser(toUser)
                    .appId(getString(content, "app_id"))
                    .page(getString(content, "page"))
                    .title(getString(content, "title"))
                    .description(getString(content, "description"))
                    .emphasisFirstItem(getBoolean(content, "emphasis_first_item"))
                    .contentItems(getStringMap(content, "content_items"))
                    .build();
            default -> message = WxCpMessage.TEXT()
                    .agentId(agentId)
                    .toUser(toUser)
                    .content(getString(content, "text"))
                    .build();
        }
        return message;
    }

    private WxCpAppChatMessage buildAppChatMessage(SendMessageRequest request, String contentType) {
        Map<String, Object> content = request.getContent();
        String chatId = request.getConversationId();

        return switch (contentType) {
            case "image" -> WxCpAppChatMessage.builder()
                    .msgType(WxConsts.KefuMsgType.IMAGE)
                    .chatId(chatId)
                    .mediaId(getString(content, "media_id"))
                    .build();
            case "file" -> WxCpAppChatMessage.builder()
                    .msgType(WxConsts.KefuMsgType.FILE)
                    .chatId(chatId)
                    .mediaId(getString(content, "media_id"))
                    .build();
            case "markdown" -> WxCpAppChatMessage.builder()
                    .msgType(WxConsts.KefuMsgType.MARKDOWN)
                    .chatId(chatId)
                    .content(getString(content, "text"))
                    .build();
            case "news" -> WxCpAppChatMessage.builder()
                    .msgType(WxConsts.KefuMsgType.NEWS)
                    .chatId(chatId)
                    .articles(buildNewsArticles(content))
                    .build();
            default -> WxCpAppChatMessage.buildTextMsg(chatId, getString(content, "text"), false);
        };
    }

    private List<NewArticle> buildNewsArticles(Map<String, Object> content) {
        Object articlesObj = content == null ? null : content.get("articles");
        if (!(articlesObj instanceof List<?> list)) {
            return List.of();
        }
        List<NewArticle> articles = new ArrayList<>();
        for (Object item : list) {
            if (!(item instanceof Map<?, ?> map)) {
                continue;
            }
            NewArticle article = NewArticle.builder()
                    .title(getString(map, "title"))
                    .description(getString(map, "description"))
                    .url(getString(map, "url"))
                    .picUrl(getString(map, "pic_url"))
                    .appid(getString(map, "app_id"))
                    .pagepath(getString(map, "page_path"))
                    .build();
            articles.add(article);
        }
        return articles;
    }

    private Integer resolveAgentId(SendMessageRequest request) {
        Map<String, Object> extra = request.getExtra();
        if (extra != null) {
            String agentId = getString(extra, "agent_id");
            if (agentId != null) {
                return Integer.valueOf(agentId);
            }
        }
        Map<String, Object> content = request.getContent();
        if (content != null) {
            String agentId = getString(content, "agent_id");
            if (agentId != null) {
                return Integer.valueOf(agentId);
            }
        }
        return null;
    }

    private String normalizeUserIds(String receiverId) {
        if (StringUtils.isBlank(receiverId)) {
            return null;
        }
        return receiverId.replace(',', '|').replaceAll("\\s+", "");
    }

    private String getString(Map<?, ?> map, String key) {
        if (map == null) {
            return null;
        }
        Object value = map.get(key);
        return value == null ? null : value.toString();
    }

    private Boolean getBoolean(Map<String, Object> map, String key) {
        if (map == null) {
            return null;
        }
        Object value = map.get(key);
        if (value instanceof Boolean b) {
            return b;
        }
        if (value instanceof Number n) {
            return n.intValue() == 1;
        }
        if (value instanceof String s) {
            return "true".equalsIgnoreCase(s) || "1".equals(s);
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    private Map<String, String> getStringMap(Map<String, Object> map, String key) {
        if (map == null) {
            return Map.of();
        }
        Object value = map.get(key);
        if (value instanceof Map<?, ?> rawMap) {
            Map<String, String> result = new java.util.HashMap<>();
            rawMap.forEach((k, v) -> result.put(Objects.toString(k, ""), v == null ? "" : v.toString()));
            return result;
        }
        return Map.of();
    }
}
