package com.sino.mci.channel.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.sino.mci.channel.entity.ChannelSubject;
import com.sino.mci.channel.messaging.RabbitConfig;
import com.sino.mci.channel.messaging.SessionArchivePullCommand;
import com.sino.mci.channel.repository.ChannelSubjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.web.bind.annotation.*;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.StringReader;
import java.util.Enumeration;

@Slf4j
@RestController
@RequestMapping("/callback/wecom")
@RequiredArgsConstructor
public class WeComCallbackController {

    private final ChannelSubjectMapper channelSubjectMapper;
    private final RabbitTemplate rabbitTemplate;

    @GetMapping
    public String verifyUrl(HttpServletRequest request) {
        log.info("WeCom callback URL verification: {}", request.getQueryString());
        return request.getParameter("echostr") != null ? request.getParameter("echostr") : "success";
    }

    @PostMapping
    public String receiveCallback(@RequestBody String body, HttpServletRequest request) {
        log.info("WeCom callback received, body length={}, query={}", body.length(), request.getQueryString());
        log.debug("WeCom callback body: {}", body);
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String name = headerNames.nextElement();
            log.debug("Header {}: {}", name, request.getHeader(name));
        }

        try {
            CallbackInfo info = parseCallbackXml(body);
            log.info("WeCom callback parsed: event={}, corpId={}", info.getEvent(), info.getCorpId());

            if ("msgaudit_notify".equalsIgnoreCase(info.getEvent())) {
                handleMsgAuditNotify(info);
            }
        } catch (Exception e) {
            log.warn("WeCom callback parse failed, still ack", e);
        }

        return "success";
    }

    private void handleMsgAuditNotify(CallbackInfo info) {
        String corpId = info.getCorpId();
        if (corpId == null || corpId.isBlank()) {
            log.warn("[WeComCallback] msgaudit_notify 缺少 corpId");
            return;
        }

        ChannelSubject subject = channelSubjectMapper.selectOne(
                new LambdaQueryWrapper<ChannelSubject>()
                        .eq(ChannelSubject::getCorpId, corpId)
                        .eq(ChannelSubject::getStatus, 1)
                        .last("LIMIT 1"));
        if (subject == null) {
            log.warn("[WeComCallback] 未找到 corpId={} 的渠道主体", corpId);
            return;
        }
        if (subject.getSessionArchiveEnabled() == null || subject.getSessionArchiveEnabled() != 1) {
            log.warn("[WeComCallback] 渠道主体未开启会话存档 corpId={}", corpId);
            return;
        }

        SessionArchivePullCommand command = new SessionArchivePullCommand();
        command.setChannelSubjectId(subject.getId());
        command.setTenantCode(subject.getTenantCode());
        command.setCorpId(subject.getCorpId());
        command.setSecret(subject.getAgentSecret());
        command.setPrivateKey(subject.getSessionArchivePrivateKey());

        rabbitTemplate.convertAndSend(RabbitConfig.SESSION_ARCHIVE_EXCHANGE,
                RabbitConfig.SESSION_ARCHIVE_ROUTING_KEY, command);
        log.info("[WeComCallback] 已发送会话存档拉取命令 subjectId={}, corpId={}", subject.getId(), corpId);
    }

    private CallbackInfo parseCallbackXml(String body) {
        CallbackInfo info = new CallbackInfo();
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document document = builder.parse(new InputSource(new StringReader(body)));
            Element root = document.getDocumentElement();
            info.setCorpId(getTagValue(root, "ToUserName"));
            info.setEvent(getTagValue(root, "Event"));
            info.setRawBody(body);
        } catch (Exception e) {
            log.warn("XML parse failed: {}", e.getMessage());
        }
        return info;
    }

    private String getTagValue(Element parent, String tagName) {
        NodeList nodeList = parent.getElementsByTagName(tagName);
        if (nodeList.getLength() == 0) {
            return null;
        }
        Node node = nodeList.item(0);
        return node.getTextContent();
    }

    private static class CallbackInfo {
        private String corpId;
        private String event;
        private String rawBody;

        public String getCorpId() {
            return corpId;
        }

        public void setCorpId(String corpId) {
            this.corpId = corpId;
        }

        public String getEvent() {
            return event;
        }

        public void setEvent(String event) {
            this.event = event;
        }

        public String getRawBody() {
            return rawBody;
        }

        public void setRawBody(String rawBody) {
            this.rawBody = rawBody;
        }
    }
}
