package com.usky.vpp.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.usky.vpp.config.VppUnTenantContext;
import com.usky.vpp.constant.VppDrEventStatus;
import com.usky.vpp.domain.VppDrEvent;
import com.usky.vpp.domain.VppDrParticipation;
import com.usky.vpp.enums.VppUnEventPhase;
import com.usky.vpp.mapper.VppDrEventMapper;
import com.usky.vpp.mapper.VppDrParticipationMapper;
import com.usky.vpp.service.VppDrEventIngestService;
import com.usky.vpp.service.VppDrExecutionBootstrapService;
import com.usky.vpp.service.VppDrUnInvitationIngestService;
import com.usky.vpp.service.VppUnDrSyncService;
import com.usky.vpp.util.VppAuditHelper;
import com.usky.vpp.util.VppUnEventParser;
import com.usky.vpp.util.VppUnPayloadHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

@Service
public class VppDrEventIngestServiceImpl implements VppDrEventIngestService {

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

    private static final int EVENT_STATUS_PENDING = VppDrEventStatus.PENDING;
    private static final int EVENT_STATUS_DECLARED = VppDrEventStatus.DECLARED;
    private static final int EVENT_STATUS_EXECUTING = VppDrEventStatus.EXECUTING;
    private static final int EVENT_STATUS_ENDED = VppDrEventStatus.ENDED;
    private static final int EVENT_STATUS_CANCELLED = VppDrEventStatus.CANCELLED;
    private static final int EVENT_STATUS_SPLITTING = VppDrEventStatus.LAST_PLACE_SPLITTING;

    @Autowired
    private VppDrEventMapper eventMapper;
    @Autowired
    private VppDrParticipationMapper participationMapper;
    @Autowired
    private VppUnDrSyncService unDrSyncService;
    @Autowired
    private VppDrExecutionBootstrapService executionBootstrapService;
    @Autowired
    private VppDrUnInvitationIngestService unInvitationIngestService;
    @Autowired
    private ObjectMapper objectMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public VppDrEvent ingestDistributeEvent(Map<String, Object> body, String source) {
        List<Map<String, Object>> events = VppUnEventParser.extractEvents(body);
        if (events.isEmpty()) {
            throw new IllegalArgumentException("DistributeEventRequest 缺少 events");
        }
        VppDrEvent last = null;
        String dnID = VppUnPayloadHelper.getString(body, "dnID");
        for (Map<String, Object> eventMap : events) {
            last = ingestSingleEvent(eventMap, body, source, dnID);
        }
        return last;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public VppDrEvent ingestInvitation(Map<String, Object> body, String source) {
        return ingestDistributeEvent(body, source);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public VppDrEvent ingestCreateCqRequest(Map<String, Object> body, String source) {
        String platformEventId = VppUnPayloadHelper.getString(body, "eventID", "eventId");
        if (!StringUtils.hasText(platformEventId)) {
            throw new IllegalArgumentException("CreateCqRequest 缺少 eventID");
        }
        VppDrEvent event = requireEvent(platformEventId);
        event.setRawPayload(toJson(body));

        @SuppressWarnings("unchecked")
        List<Map<String, Object>> list = body.get("list") instanceof List
                ? (List<Map<String, Object>>) body.get("list") : null;
        if (list != null && !list.isEmpty()) {
            unDrSyncService.syncOptContent(event, list, true);
            BigDecimal total = sumListLoad(list);
            if (total.compareTo(BigDecimal.ZERO) > 0) {
                event.setClearedCapacityKw(total);
            }
        }
        if (event.getEventStatus() == null || event.getEventStatus() == EVENT_STATUS_PENDING) {
            event.setEventStatus(EVENT_STATUS_DECLARED);
        }
        VppAuditHelper.fillUpdate(event);
        eventMapper.updateById(event);
        log.info("[{}] 处理 CreateCqRequest eventId={}", source, platformEventId);
        return event;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public VppDrEvent ingestCreateOptRequest(Map<String, Object> body, String source) {
        String platformEventId = VppUnPayloadHelper.getString(body, "eventID", "eventId");
        if (!StringUtils.hasText(platformEventId)) {
            throw new IllegalArgumentException("CreateOptRequest 缺少 eventID");
        }
        VppDrEvent event = findByPlatformEventId(platformEventId);
        if (event == null) {
            throw new IllegalArgumentException("事件不存在: " + platformEventId);
        }
        event.setRawPayload(toJson(body));

        String optType = VppUnPayloadHelper.getString(body, "optType");
        if ("optOut".equalsIgnoreCase(optType)) {
            event.setEventStatus(EVENT_STATUS_CANCELLED);
            VppAuditHelper.fillUpdate(event);
            eventMapper.updateById(event);
            log.info("[{}] optOut 取消事件 eventId={}", source, platformEventId);
            return event;
        }

        @SuppressWarnings("unchecked")
        List<Map<String, Object>> list = body.get("list") instanceof List
                ? (List<Map<String, Object>>) body.get("list") : null;
        if (list != null && !list.isEmpty()) {
            unDrSyncService.syncOptContent(event, list, false);
            event.setEventStatus(EVENT_STATUS_DECLARED);
        }
        VppAuditHelper.fillUpdate(event);
        eventMapper.updateById(event);
        log.info("[{}] 处理 CreateOptRequest eventId={}", source, platformEventId);
        return event;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public VppDrEvent acknowledgeClearingPublicity(String platformEventId, Map<String, Object> body, String source) {
        VppDrEvent event = requireEvent(platformEventId);
        event.setRawPayload(toJson(body));
        if (event.getEventStatus() == null || event.getEventStatus() <= EVENT_STATUS_DECLARED) {
            event.setEventStatus(EVENT_STATUS_EXECUTING);
        }
        executionBootstrapService.ensureExecutions(event);
        VppAuditHelper.fillUpdate(event);
        eventMapper.updateById(event);
        log.info("[{}] 出清公示已确认 eventId={}", source, platformEventId);
        return event;
    }

    private VppDrEvent ingestSingleEvent(Map<String, Object> eventMap, Map<String, Object> rawBody, String source, String dnID) {
        Map<String, Object> descriptor = VppUnEventParser.getDescriptor(eventMap);
        String platformEventId = VppUnEventParser.getEventId(eventMap);
        if (!StringUtils.hasText(platformEventId)) {
            throw new IllegalArgumentException("Event 缺少 descriptor.eventID");
        }

        Integer platformStatus = VppUnEventParser.mapPlatformStatus(descriptor);
        if (platformStatus != null && platformStatus == EVENT_STATUS_CANCELLED) {
            return cancelEvent(findByPlatformEventId(platformEventId), platformEventId, rawBody, source);
        }

        VppUnEventPhase phase = VppUnEventParser.detectPhase(eventMap);
        VppDrEvent event = findByPlatformEventId(platformEventId);
        if (event == null) {
            event = new VppDrEvent();
            event.setEventId(platformEventId);
            event.setEventStatus(EVENT_STATUS_PENDING);
            applyBaseFields(event, eventMap, descriptor);
            event.setRawPayload(toJson(rawBody));
            Integer tenantId = VppUnTenantContext.resolveTenantId();
            if (tenantId != null) {
                event.setTenantId(tenantId);
            }
            VppAuditHelper.fillCreate(event);
            eventMapper.insert(event);
            log.info("[{}] 新建事件 eventId={}, phase={}", source, platformEventId, phase);
        } else {
            applyBaseFields(event, eventMap, descriptor);
            event.setRawPayload(toJson(rawBody));
            VppAuditHelper.fillUpdate(event);
            eventMapper.updateById(event);
            log.info("[{}] 更新事件 eventId={}, phase={}", source, platformEventId, phase);
        }

        applyPhase(event, eventMap, phase, platformStatus);
        if (phase == VppUnEventPhase.INVITATION) {
            unInvitationIngestService.syncInvitationsFromUnEvent(event, eventMap);
        }
        VppAuditHelper.fillUpdate(event);
        eventMapper.updateById(event);
        return event;
    }

    private void applyPhase(VppDrEvent event, Map<String, Object> eventMap, VppUnEventPhase phase, Integer platformStatus) {
        List<Map<String, Object>> resources = VppUnEventParser.getTargetResources(eventMap);
        switch (phase) {
            case INVITATION:
                event.setEventStatus(EVENT_STATUS_PENDING);
                break;
            case DECLARE_FEEDBACK:
                event.setEventStatus(EVENT_STATUS_DECLARED);
                unDrSyncService.syncTargetResources(event, resources, false);
                break;
            case SPLIT_NOTICE:
                event.setEventStatus(EVENT_STATUS_SPLITTING);
                event.setTargetCapacityKw(VppUnEventParser.sumResourceLoadKw(resources, true));
                deleteEventParticipations(event);
                break;
            case SPLIT_RESULT:
                event.setEventStatus(EVENT_STATUS_DECLARED);
                unDrSyncService.syncTargetResources(event, resources, true);
                break;
            case CLEARING_PUBLICITY:
                BigDecimal cleared = VppUnEventParser.sumResourceLoadKw(resources, true);
                if (cleared.compareTo(BigDecimal.ZERO) > 0) {
                    event.setClearedCapacityKw(cleared);
                }
                unDrSyncService.syncTargetResources(event, resources, true);
                event.setEventStatus(EVENT_STATUS_DECLARED);
                break;
            default:
                break;
        }
        if (platformStatus != null) {
            if (platformStatus == EVENT_STATUS_EXECUTING) {
                event.setEventStatus(EVENT_STATUS_EXECUTING);
            } else if (platformStatus == EVENT_STATUS_ENDED) {
                event.setEventStatus(EVENT_STATUS_ENDED);
            }
        }
    }

    private void deleteEventParticipations(VppDrEvent event) {
        if (event == null || event.getId() == null) {
            return;
        }
        List<VppDrParticipation> participations = participationMapper.selectList(
                new LambdaQueryWrapper<VppDrParticipation>()
                        .eq(VppDrParticipation::getEventId, event.getId())
                        .eq(VppDrParticipation::getDeleteFlag, VppAuditHelper.NOT_DELETED));
        for (VppDrParticipation participation : participations) {
            VppAuditHelper.fillSoftDelete(participation);
            participationMapper.updateById(participation);
        }
    }

    private void applyBaseFields(VppDrEvent event, Map<String, Object> eventMap, Map<String, Object> descriptor) {
        String comment = VppUnPayloadHelper.getString(descriptor, "comment");
        LocalDateTime replyDeadline = VppUnPayloadHelper.getDateTime(descriptor, "deadline");
        event.setEventName(StringUtils.hasText(comment) ? comment : event.getEventId());
        event.setDeclareDeadline(replyDeadline);
        event.setResponseType(VppUnEventParser.mapResponseType(descriptor));
        event.setEventType(VppUnEventParser.mapEventType(descriptor));

        LocalDateTime[] period = VppUnEventParser.resolveActivePeriod(eventMap);
        if (period[0] != null) {
            event.setStartTime(period[0]);
        } else if (event.getStartTime() == null) {
            throw new IllegalArgumentException("Event 缺少 activePeriod.dtstart");
        }
        if (period[1] != null) {
            event.setEndTime(period[1]);
        } else if (event.getEndTime() == null && event.getStartTime() != null) {
            event.setEndTime(event.getStartTime().plusHours(2));
        }

        BigDecimal demandKw = VppUnEventParser.extractDemandChargeKw(eventMap);
        if (demandKw != null) {
            event.setTargetCapacityKw(demandKw);
        } else if (event.getTargetCapacityKw() == null) {
            event.setTargetCapacityKw(BigDecimal.ZERO);
        }

        BigDecimal energyPrice = VppUnEventParser.extractEnergyPrice(eventMap);
        if (energyPrice != null) {
            event.setSubsidyPrice(energyPrice);
        }
    }

    private VppDrEvent cancelEvent(VppDrEvent existing, String platformEventId, Map<String, Object> body, String source) {
        if (existing == null) {
            existing = new VppDrEvent();
            existing.setEventId(platformEventId);
            existing.setEventName(platformEventId);
            existing.setResponseType(1);
            existing.setStartTime(LocalDateTime.now());
            existing.setEndTime(LocalDateTime.now());
            existing.setTargetCapacityKw(BigDecimal.ZERO);
            existing.setRawPayload(toJson(body));
            existing.setEventStatus(EVENT_STATUS_CANCELLED);
            VppAuditHelper.fillCreate(existing);
            eventMapper.insert(existing);
        } else {
            existing.setEventStatus(EVENT_STATUS_CANCELLED);
            existing.setRawPayload(toJson(body));
            VppAuditHelper.fillUpdate(existing);
            eventMapper.updateById(existing);
        }
        log.info("[{}] 取消事件 eventId={}", source, platformEventId);
        return existing;
    }

    private BigDecimal sumListLoad(List<Map<String, Object>> list) {
        BigDecimal total = BigDecimal.ZERO;
        for (Map<String, Object> item : list) {
            BigDecimal load = VppUnPayloadHelper.getDecimal(item, "load");
            if (load != null) {
                total = total.add(load.abs());
            }
        }
        return total;
    }

    private VppDrEvent findByPlatformEventId(String platformEventId) {
        return eventMapper.selectOne(new LambdaQueryWrapper<VppDrEvent>()
                .eq(VppDrEvent::getEventId, platformEventId)
                .eq(VppDrEvent::getDeleteFlag, VppAuditHelper.NOT_DELETED)
                .last("LIMIT 1"));
    }

    private VppDrEvent requireEvent(String platformEventId) {
        VppDrEvent event = findByPlatformEventId(platformEventId);
        if (event == null) {
            throw new IllegalArgumentException("事件不存在: " + platformEventId);
        }
        return event;
    }

    private String toJson(Map<String, Object> body) {
        if (body == null) {
            return null;
        }
        try {
            return objectMapper.writeValueAsString(body);
        } catch (JsonProcessingException ex) {
            return String.valueOf(body);
        }
    }
}
