package cn.axzo.msg.center.dal;

import cn.axzo.im.center.api.vo.PersonAccountAttribute;
import cn.axzo.im.center.api.vo.resp.MessageTaskResp;
import cn.axzo.im.center.api.vo.resp.UpdatableMessageSendResult;
import cn.axzo.msg.center.dal.mapper.CardMapper;
import cn.axzo.msg.center.domain.entity.Card;
import cn.axzo.msg.center.domain.persistence.BaseEntityExt;
import cn.axzo.msg.center.service.enums.CardState;
import cn.axzo.msg.center.util.DeleteAwareInterceptor;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;

/**
 * @author yanglin
 */
@Slf4j
@Component
public class CardDao extends ServiceImpl<CardMapper, Card> {

    public List<Card> collectPersonCards(String oneOfBizMessageId) {
        Card card = findCardByBizMessageId(oneOfBizMessageId).orElse(null);
        if (card == null) return Collections.emptyList();
        return lambdaQuery()
                .eq(Card::getAppCode, card.getAppCode())
                .eq(Card::getTemplateCode, card.getTemplateCode())
                .eq(Card::getBizCode, card.getBizCode())
                .eq(Card::getSubBizCode, card.getSubBizCode())
                .eq(Card::getReceiverOuId, card.getReceiverOuId())
                .eq(Card::getReceiverWorkspaceId, card.getReceiverWorkspaceId())
                .eq(Card::getReceiverPersonId, card.getReceiverPersonId())
                .list();
    }

    public Optional<Card> findCardByBizMessageId(String bizMessageId) {
        return Optional.ofNullable(lambdaQuery()
                .eq(Card::getBizMessageId, bizMessageId)
                .one());
    }

    // 外层有事务, 这里主要是为了suppress idea的警告
    @Transactional
    public void setSendSuccess(List<Card> cards, MessageTaskResp imResponse) {
        ArrayList<Card> updates = new ArrayList<>();
        List<UpdatableMessageSendResult> updatableResults = imResponse.getUpdatableMessageSendResults();
        if (updatableResults == null)
            updatableResults = Collections.emptyList();
        Map<PersonAccountAttribute, String> account2BizMessageId = updatableResults.stream()
                .collect(toMap(UpdatableMessageSendResult::getAccount, UpdatableMessageSendResult::getBizMessageId));
        for (Card card : cards) {
            PersonAccountAttribute key = new PersonAccountAttribute();
            key.setPersonId(card.getReceiverPersonId() + "");
            key.setOuId(card.getReceiverOuId());
            key.setWorkspaceId(card.getReceiverWorkspaceId());
            key.setAppType(card.getReceiverAppType());

            Card update = new Card();
            update.setId(card.getId());
            update.setImTaskId(imResponse.getId());
            update.setCardState(CardState.SEND_SUCCESS);
            String bizMessageId = account2BizMessageId.get(key);
            if (bizMessageId != null)
                update.setBizMessageId(bizMessageId);
            updates.add(update);
        }
        updateBatchById(updates);
    }

    public List<Card> reloadCardsForUpdate(List<Card> cards) {
        if (CollectionUtils.isEmpty(cards))
            return Collections.emptyList();
        return DeleteAwareInterceptor.execute(() -> lambdaQuery()
                .in(Card::getId, collectCardIdsSorted(cards))
                .last("FOR UPDATE")
                .list());
    }

    public List<Card> reloadCards(List<Card> cards) {
        if (CollectionUtils.isEmpty(cards))
            return Collections.emptyList();
        return DeleteAwareInterceptor.execute(() ->
                listByIds(collectCardIdsSorted(cards)));
    }

    public void deleteCards(List<Card> cards) {
        if (CollectionUtils.isEmpty(cards))
            return;
        removeByIds(collectCardIdsSorted(cards));
    }

    private static List<Long> collectCardIdsSorted(List<Card> cards) {
        return cards.stream()
                .map(BaseEntityExt::getId)
                .sorted()
                .collect(toList());
    }
}