package com.gxwebsoft.glt.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.gxwebsoft.glt.entity.GltTicketOrder;
import com.gxwebsoft.glt.entity.GltTicketTemplate;
import com.gxwebsoft.glt.entity.GltUserTicket;
import com.gxwebsoft.glt.entity.GltUserTicketLog;
import com.gxwebsoft.glt.entity.GltUserTicketRelease;
import com.gxwebsoft.shop.entity.ShopOrder;
import com.gxwebsoft.shop.entity.ShopOrderGoods;
import com.gxwebsoft.shop.entity.ShopUserAddress;
import com.gxwebsoft.shop.service.ShopOrderGoodsService;
import com.gxwebsoft.shop.service.ShopOrderService;
import com.gxwebsoft.shop.service.ShopUserAddressService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * 套票发放（从订单生成用户套票 + 释放计划）的业务逻辑。
 *
 * 说明：
 * - 定时任务无登录态时，MyBatis-Plus 多租户插件可能拿不到 tenantId；
 *   外层任务方法会通过 @IgnoreTenant 禁用租户拦截，本服务内部强制用 tenantId 过滤。
 * - 幂等：以 (tenantId, templateId, orderNo, orderGoodsId) 判断是否已发放。
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class GltTicketIssueService {

    public static final int CHANGE_TYPE_ISSUE = 10;
    /** 变更类型：起始送水自动核销（按模板 startSendQty 在发放时自动消耗） */
    public static final int CHANGE_TYPE_START_SEND_WRITE_OFF = 12;

    private enum IssueOutcome {
        ISSUED,
        ALREADY_ISSUED,
        SKIPPED,
        NO_TEMPLATE
    }

    private final ShopOrderService shopOrderService;
    private final ShopOrderGoodsService shopOrderGoodsService;

    private final GltTicketTemplateService gltTicketTemplateService;
    private final GltUserTicketService gltUserTicketService;
    private final GltUserTicketReleaseService gltUserTicketReleaseService;
    private final GltUserTicketLogService gltUserTicketLogService;
    private final GltTicketOrderService gltTicketOrderService;
    private final ShopUserAddressService shopUserAddressService;
    private final TransactionTemplate transactionTemplate;

    /**
     * 扫描“今日订单”，执行套票发放。
     */
    public void issueTodayOrders(Integer tenantId, Integer formId) {
        if (formId == null) {
            return;
        }
        issueTodayOrders(tenantId, List.of(formId));
    }

    /**
     * 扫描“今日订单”，执行套票发放（支持多个商品/模板）。
     */
    public void issueTodayOrders(Integer tenantId, List<Integer> goodsIds) {
        if (tenantId == null || goodsIds == null || goodsIds.isEmpty()) {
            return;
        }
        List<Integer> uniqueGoodsIds = goodsIds.stream()
            .filter(Objects::nonNull)
            .distinct()
            .toList();
        if (uniqueGoodsIds.isEmpty()) {
            return;
        }
        Set<Integer> goodsIdSet = new HashSet<>(uniqueGoodsIds);

        LocalDateTime todayStart = LocalDate.now().atStartOfDay();
        LocalDateTime tomorrowStart = todayStart.plusDays(1);

        List<ShopOrder> orders = shopOrderService.list(
            new LambdaQueryWrapper<ShopOrder>()
                .eq(ShopOrder::getTenantId, tenantId)
                .in(ShopOrder::getFormId, uniqueGoodsIds)
                .eq(ShopOrder::getPayStatus, true)
                .eq(ShopOrder::getOrderStatus, 0)
                // 今日订单（兼容：以 create_time 或 pay_time 任一落在今日即可）
                .and(w -> w
                    .ge(ShopOrder::getCreateTime, todayStart).lt(ShopOrder::getCreateTime, tomorrowStart)
                    .or()
                    .ge(ShopOrder::getPayTime, todayStart).lt(ShopOrder::getPayTime, tomorrowStart)
                )
                .orderByAsc(ShopOrder::getPayTime)
                .orderByAsc(ShopOrder::getOrderId)
        );

        if (orders.isEmpty()) {
            log.debug("套票发放扫描：今日无符合条件的订单 tenantId={}, goodsIds={}", tenantId, uniqueGoodsIds);
            return;
        }

        int success = 0;
        int skipped = 0;
        int failed = 0;

        for (ShopOrder order : orders) {
            try {
                int issuedCount = issueForOrder(tenantId, goodsIdSet, order);
                if (issuedCount > 0) {
                    success += issuedCount;
                } else {
                    skipped++;
                }
            } catch (Exception e) {
                failed++;
                log.error("套票发放失败 - tenantId={}, orderNo={}, orderId={}",
                    tenantId, order.getOrderNo(), order.getOrderId(), e);
            }
        }

        log.info("套票发放扫描完成 - tenantId={}, goodsIds={}, 订单数={}, 发放成功={}, 跳过={}, 失败={}",
            tenantId, uniqueGoodsIds, orders.size(), success, skipped, failed);
    }

    private int issueForOrder(Integer tenantId, Set<Integer> goodsIds, ShopOrder order) {
        List<ShopOrderGoods> goodsList = shopOrderGoodsService.getListByOrderIdIgnoreTenant(order.getOrderId());
        if (goodsList == null || goodsList.isEmpty()) {
            return 0;
        }

        int issuedCount = 0; // 本轮新增发放数量（用于统计）
        boolean shouldCompleteOrder = false;

        for (ShopOrderGoods og : goodsList) {
            if (og.getGoodsId() == null || !goodsIds.contains(og.getGoodsId())) {
                continue;
            }

            IssueOutcome outcome = transactionTemplate.execute(status -> doIssueOne(tenantId, order, og));
            if (outcome == IssueOutcome.ISSUED) {
                issuedCount++;
                shouldCompleteOrder = true;
            } else if (outcome == IssueOutcome.ALREADY_ISSUED) {
                // 幂等：已处理过也应视为完成，避免重复扫描
                shouldCompleteOrder = true;
            }
        }

        if (shouldCompleteOrder) {
            LocalDateTime now = LocalDateTime.now();
            // 任务执行完后将订单置为“已完成”：order_status=1
            shopOrderService.update(
                new LambdaUpdateWrapper<ShopOrder>()
                    .eq(ShopOrder::getOrderId, order.getOrderId())
                    .eq(ShopOrder::getTenantId, tenantId)
                    .set(ShopOrder::getHasTakeGift, true)
                    .set(ShopOrder::getUpdateTime, now)
            );
        }

        return issuedCount;
    }

    private IssueOutcome doIssueOne(Integer tenantId, ShopOrder order, ShopOrderGoods og) {
        if (order.getUserId() == null) {
            log.warn("套票发放跳过：订单缺少 userId - tenantId={}, orderNo={}", tenantId, order.getOrderNo());
            return IssueOutcome.SKIPPED;
        }
        if (og.getId() == null) {
            log.warn("套票发放跳过：订单商品缺少 id - tenantId={}, orderNo={}", tenantId, order.getOrderNo());
            return IssueOutcome.SKIPPED;
        }

        GltTicketTemplate template = gltTicketTemplateService.getOne(
            new LambdaQueryWrapper<GltTicketTemplate>()
                .eq(GltTicketTemplate::getTenantId, tenantId)
                .eq(GltTicketTemplate::getGoodsId, og.getGoodsId())
                .eq(GltTicketTemplate::getDeleted, 0)
                .last("limit 1")
        );

        if (template == null) {
            log.warn("套票发放跳过：未配置套票模板 - tenantId={}, goodsId={}, orderNo={}",
                tenantId, og.getGoodsId(), order.getOrderNo());
            return IssueOutcome.NO_TEMPLATE;
        }
        if (!Boolean.TRUE.equals(template.getEnabled())) {
            log.info("套票发放跳过：套票模板未启用 - tenantId={}, templateId={}, goodsId={}",
                tenantId, template.getId(), template.getGoodsId());
            return IssueOutcome.SKIPPED;
        }

        // 幂等：同一 orderNo + orderGoodsId 只发放一次
        GltUserTicket existing = gltUserTicketService.getOne(
            new LambdaQueryWrapper<GltUserTicket>()
                .eq(GltUserTicket::getTenantId, tenantId)
                .eq(GltUserTicket::getTemplateId, template.getId())
                .eq(GltUserTicket::getOrderNo, order.getOrderNo())
                .eq(GltUserTicket::getOrderGoodsId, og.getId())
                .eq(GltUserTicket::getDeleted, 0)
                .last("limit 1")
        );
        if (existing != null) {
            log.debug("套票已发放，跳过 - tenantId={}, orderNo={}, orderGoodsId={}, userTicketId={}",
                tenantId, order.getOrderNo(), og.getId(), existing.getId());
            return IssueOutcome.ALREADY_ISSUED;
        }

        int buyQty = og.getTotalNum() != null ? og.getTotalNum() : 0;
        if (buyQty <= 0) {
            log.warn("套票发放跳过：购买数量无效 - tenantId={}, orderNo={}, orderGoodsId={}, totalNum={}",
                tenantId, order.getOrderNo(), og.getId(), og.getTotalNum());
            return IssueOutcome.SKIPPED;
        }

        Integer minBuyQty = template.getMinBuyQty();
        if (minBuyQty != null && minBuyQty > 0 && buyQty < minBuyQty) {
            log.info("套票发放跳过：未达到最小购买数量 - tenantId={}, orderNo={}, buyQty={}, minBuyQty={}",
                tenantId, order.getOrderNo(), buyQty, minBuyQty);
            return IssueOutcome.SKIPPED;
        }

        int giftMultiplier = template.getGiftMultiplier() != null ? template.getGiftMultiplier() : 0;
        int giftQty = buyQty * Math.max(giftMultiplier, 0);

        // 购买量（buyQty）应立即可用；赠送量（giftQty）进入冻结并按计划释放。
        int totalQty = buyQty + giftQty;

        if (totalQty <= 0) {
            log.info("套票发放跳过：计算结果为0 - tenantId={}, orderNo={}, buyQty={}, giftMultiplier={}, includeBuyQty={}",
                tenantId, order.getOrderNo(), buyQty, giftMultiplier, template.getIncludeBuyQty());
            return IssueOutcome.SKIPPED;
        }

        LocalDateTime now = LocalDateTime.now();

        GltUserTicket userTicket = new GltUserTicket();
        userTicket.setTemplateId(template.getId());
        userTicket.setGoodsId(og.getGoodsId());
        userTicket.setOrderId(order.getOrderId());
        userTicket.setOrderNo(order.getOrderNo());
        userTicket.setOrderGoodsId(og.getId());
        userTicket.setTotalQty(totalQty);
        userTicket.setAvailableQty(buyQty);
        userTicket.setFrozenQty(giftQty);
        userTicket.setUsedQty(0);
        // 初始可用量来自“购买量”，视为已释放
        userTicket.setReleasedQty(buyQty);
        userTicket.setOrderGoodsQty(og.getTotalNum());
        userTicket.setUserId(order.getUserId());
        userTicket.setSortNumber(0);
        userTicket.setComments("订单发放套票");
        userTicket.setStatus(0);
        userTicket.setDeleted(0);
        userTicket.setTenantId(tenantId);
        userTicket.setCreateTime(now);
        userTicket.setUpdateTime(now);

        gltUserTicketService.save(userTicket);

        // 生成释放计划（按月）
        LocalDateTime baseTime = order.getPayTime() != null ? order.getPayTime() : order.getCreateTime();
        if (baseTime == null) {
            baseTime = now;
        }
        List<GltUserTicketRelease> releases = buildReleasePlan(template, userTicket, baseTime, giftQty, now);
        if (!releases.isEmpty()) {
            gltUserTicketReleaseService.saveBatch(releases);
        }

        // 发放流水
        GltUserTicketLog issueLog = new GltUserTicketLog();
        issueLog.setUserTicketId(userTicket.getId());
        issueLog.setChangeType(CHANGE_TYPE_ISSUE);
        issueLog.setChangeAvailable(buyQty);
        issueLog.setChangeFrozen(giftQty);
        issueLog.setChangeUsed(0);
        issueLog.setAvailableAfter(buyQty);
        issueLog.setFrozenAfter(giftQty);
        issueLog.setUsedAfter(0);
        issueLog.setOrderId(order.getOrderId());
        issueLog.setOrderNo(order.getOrderNo());
        issueLog.setUserId(order.getUserId());
        issueLog.setSortNumber(0);
        issueLog.setComments("套票发放");
        issueLog.setStatus(0);
        issueLog.setDeleted(0);
        issueLog.setTenantId(tenantId);
        issueLog.setCreateTime(now);
        issueLog.setUpdateTime(now);
        gltUserTicketLogService.save(issueLog);

        // 按模板配置：自动“使用掉第一次水票”（起始送水数量）
        Integer startSendQtyObj = template.getStartSendQty();
        int startSendQty = startSendQtyObj != null ? startSendQtyObj : 0;
        if (startSendQty > 0) {
            int availableBefore = userTicket.getAvailableQty() != null ? userTicket.getAvailableQty() : 0;
            int usedBefore = userTicket.getUsedQty() != null ? userTicket.getUsedQty() : 0;
            int toUse = Math.min(startSendQty, availableBefore);
            if (toUse > 0) {
                userTicket.setAvailableQty(availableBefore - toUse);
                userTicket.setUsedQty(usedBefore + toUse);
                userTicket.setUpdateTime(now);
                if (!gltUserTicketService.updateById(userTicket)) {
                    throw new IllegalStateException("起始送水自动核销：更新用户水票失败 userTicketId=" + userTicket.getId());
                }

                // 起始送水：自动核销成功后，生成一条送水订单（用于配送端/后台跟踪）
                GltTicketOrder ticketOrder = buildStartSendTicketOrder(tenantId, order, userTicket, toUse, now);
                if (!gltTicketOrderService.save(ticketOrder)) {
                    throw new IllegalStateException("起始送水自动核销：创建送水订单失败 userTicketId=" + userTicket.getId());
                }

                GltUserTicketLog writeOffLog = new GltUserTicketLog();
                writeOffLog.setUserTicketId(userTicket.getId());
                writeOffLog.setChangeType(CHANGE_TYPE_START_SEND_WRITE_OFF);
                writeOffLog.setChangeAvailable(-toUse);
                writeOffLog.setChangeFrozen(0);
                writeOffLog.setChangeUsed(toUse);
                writeOffLog.setAvailableAfter(userTicket.getAvailableQty());
                writeOffLog.setFrozenAfter(userTicket.getFrozenQty() != null ? userTicket.getFrozenQty() : 0);
                writeOffLog.setUsedAfter(userTicket.getUsedQty());
                // 关联送水订单（保持与“用户下单核销”的日志一致），并在备注里保留来源商城订单号便于追溯
                writeOffLog.setOrderId(ticketOrder.getId());
                writeOffLog.setOrderNo(ticketOrder.getId() == null ? null : String.valueOf(ticketOrder.getId()));
                writeOffLog.setUserId(order.getUserId());
                writeOffLog.setSortNumber(0);
                writeOffLog.setComments("起始送水自动核销(来源商城订单:" + safe(order.getOrderNo()) + ")");
                writeOffLog.setStatus(0);
                writeOffLog.setDeleted(0);
                writeOffLog.setTenantId(tenantId);
                writeOffLog.setCreateTime(now);
                writeOffLog.setUpdateTime(now);
                gltUserTicketLogService.save(writeOffLog);
            }
        }

        log.info("套票发放成功 - tenantId={}, orderNo={}, orderGoodsId={}, templateId={}, userTicketId={}, totalQty={}",
            tenantId, order.getOrderNo(), og.getId(), template.getId(), userTicket.getId(), totalQty);

        return IssueOutcome.ISSUED;
    }

    private List<GltUserTicketRelease> buildReleasePlan(GltTicketTemplate template,
                                                       GltUserTicket userTicket,
                                                       LocalDateTime baseTime,
                                                       int totalQty,
                                                       LocalDateTime now) {
        List<GltUserTicketRelease> list = new ArrayList<>();

        if (totalQty <= 0) {
            return list;
        }

        // 首期释放时间
        LocalDateTime firstReleaseTime;
        if (Objects.equals(template.getFirstReleaseMode(), 1)) {
            firstReleaseTime = nextMonthSameDay(baseTime);
        } else {
            firstReleaseTime = baseTime;
        }

        // 每期释放数量计算
        Integer releasePeriods = template.getReleasePeriods();
        if (releasePeriods != null && releasePeriods > 0) {
            int base = totalQty / releasePeriods;
            int remainder = totalQty % releasePeriods;
            for (int i = 1; i <= releasePeriods; i++) {
                int qty = base + (i <= remainder ? 1 : 0);
                if (qty <= 0) {
                    continue;
                }
                list.add(buildRelease(userTicket, i, qty, firstReleaseTime.plusMonths(i - 1), now));
            }
            return list;
        }

        int monthlyReleaseQty = template.getMonthlyReleaseQty() != null && template.getMonthlyReleaseQty() > 0
            ? template.getMonthlyReleaseQty()
            : 10;
        int periods = (totalQty + monthlyReleaseQty - 1) / monthlyReleaseQty;
        int remaining = totalQty;
        for (int i = 1; i <= periods; i++) {
            int qty = Math.min(monthlyReleaseQty, remaining);
            if (qty <= 0) {
                break;
            }
            remaining -= qty;
            list.add(buildRelease(userTicket, i, qty, firstReleaseTime.plusMonths(i - 1), now));
        }

        return list;
    }

    private GltUserTicketRelease buildRelease(GltUserTicket userTicket,
                                             int periodNo,
                                             int releaseQty,
                                             LocalDateTime releaseTime,
                                             LocalDateTime now) {
        GltUserTicketRelease r = new GltUserTicketRelease();
        r.setUserTicketId(userTicket.getId() != null ? userTicket.getId().longValue() : null);
        r.setUserId(userTicket.getUserId());
        r.setPeriodNo(periodNo);
        r.setReleaseQty(releaseQty);
        r.setReleaseTime(releaseTime);
        r.setStatus(0);
        r.setDeleted(0);
        r.setTenantId(userTicket.getTenantId());
        r.setCreateTime(now);
        r.setUpdateTime(now);
        return r;
    }

    private static LocalDateTime nextMonthSameDay(LocalDateTime baseTime) {
        LocalDate baseDate = baseTime.toLocalDate();
        LocalTime time = baseTime.toLocalTime();

        LocalDate nextMonth = baseDate.plusMonths(1);
        int day = Math.min(baseDate.getDayOfMonth(), nextMonth.lengthOfMonth());
        LocalDate adjusted = nextMonth.withDayOfMonth(day);
        return LocalDateTime.of(adjusted, time);
    }

    private static final DateTimeFormatter DATETIME_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    private GltTicketOrder buildStartSendTicketOrder(Integer tenantId,
                                                     ShopOrder shopOrder,
                                                     GltUserTicket userTicket,
                                                     int totalNum,
                                                     LocalDateTime now) {
        GltTicketOrder o = new GltTicketOrder();
        o.setUserTicketId(userTicket.getId());
        o.setUserId(shopOrder.getUserId());
        o.setStoreId(shopOrder.getStoreId());
        o.setWarehouseId(shopOrder.getWarehouseId());
        o.setRiderId(shopOrder.getRiderId());
        o.setTotalNum(totalNum);
        o.setPrice(BigDecimal.ZERO);
        o.setBuyerRemarks(shopOrder.getBuyerRemarks());

        // 地址快照：优先使用地址表快照；兜底使用商城订单上的 address 字段
        Integer addressId = shopOrder.getAddressId();
        o.setAddressId(addressId);
        String addressSnapshot = null;
        if (addressId != null) {
            ShopUserAddress addr = shopUserAddressService.getOne(new LambdaQueryWrapper<ShopUserAddress>()
                .eq(ShopUserAddress::getId, addressId)
                .eq(ShopUserAddress::getUserId, shopOrder.getUserId())
                .eq(ShopUserAddress::getTenantId, tenantId)
                .last("limit 1"));
            addressSnapshot = buildAddressSnapshot(addr);
        }
        if (addressSnapshot == null || addressSnapshot.isBlank()) {
            addressSnapshot = shopOrder.getAddress();
        }
        o.setAddress(addressSnapshot);

        String preferredSendTime = shopOrder.getSendStartTime();
        if (preferredSendTime == null || preferredSendTime.isBlank()) {
            preferredSendTime = now.format(DATETIME_FMT);
        }
        o.setSendTime(preferredSendTime);
        o.setDeliveryStatus(GltTicketOrderService.DELIVERY_STATUS_WAITING);
        o.setSortNumber(0);
        o.setComments("起始送水自动下单(来源商城订单:" + safe(shopOrder.getOrderNo()) + ")");
        o.setStatus(0);
        o.setDeleted(0);
        o.setTenantId(tenantId);
        o.setCreateTime(now);
        o.setUpdateTime(now);
        return o;
    }

    private static String buildAddressSnapshot(ShopUserAddress addr) {
        if (addr == null) {
            return null;
        }
        if (addr.getFullAddress() != null && !addr.getFullAddress().isBlank()) {
            return addr.getFullAddress();
        }
        // 兼容旧数据：fullAddress 为空时，拼接省市区 + 详细地址
        StringBuilder sb = new StringBuilder();
        if (addr.getProvince() != null) sb.append(addr.getProvince());
        if (addr.getCity() != null) sb.append(addr.getCity());
        if (addr.getRegion() != null) sb.append(addr.getRegion());
        if (addr.getAddress() != null) sb.append(addr.getAddress());
        String s = sb.toString();
        if (!s.isBlank()) {
            return s;
        }
        return addr.getAddress();
    }

    private static String safe(String s) {
        return s == null ? "" : s;
    }
}
