package com.erp.warehouse.service.impl;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.erp.warehouse.dto.StockAlertDTO.*;
import com.erp.warehouse.entity.Stock;
import com.erp.warehouse.entity.StockAlertConfig;
import com.erp.warehouse.entity.StockAlertNotification;
import com.erp.warehouse.entity.Warehouse;
import com.erp.warehouse.mapper.StockAlertConfigMapper;
import com.erp.warehouse.mapper.StockAlertNotificationMapper;
import com.erp.warehouse.mapper.StockMapper;
import com.erp.warehouse.mapper.WarehouseMapper;
import com.erp.warehouse.service.StockAlertService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 库存预警服务实现
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class StockAlertServiceImpl implements StockAlertService {

    private final StockMapper stockMapper;
    private final WarehouseMapper warehouseMapper;
    private final StockAlertConfigMapper alertConfigMapper;
    private final StockAlertNotificationMapper alertNotificationMapper;

    private static final int DEFAULT_THRESHOLD = 10;

    // ==================== 预警列表 ====================

    @Override
    public IPage<AlertItem> listAlerts(Long tenantId, Long warehouseId, String keyword, String alertLevel, int page, int limit) {
        StockAlertConfig config = getOrCreateConfig(tenantId);
        int globalThreshold = config.getGlobalThreshold() != null ? config.getGlobalThreshold() : DEFAULT_THRESHOLD;

        Page<Stock> pageParam = new Page<>(page, limit);
        LambdaQueryWrapper<Stock> queryWrapper = new LambdaQueryWrapper<>();

        // 预警条件: (quantity - locked - reserved - defective) <= LEAST(warning_threshold, globalThreshold)
        queryWrapper.apply("""
            (COALESCE(quantity, 0) - COALESCE(locked_quantity, 0) - COALESCE(reserved_quantity, 0) - COALESCE(defective_quantity, 0))
            <= LEAST(COALESCE(warning_threshold, 0), {0})
            """, globalThreshold);

        if (warehouseId != null) {
            queryWrapper.eq(Stock::getWarehouseId, warehouseId);
        }
        if (keyword != null && !keyword.isEmpty()) {
            queryWrapper.and(w -> w.like(Stock::getSkuCode, keyword).or().like(Stock::getSkuName, keyword));
        }

        // 预警级别筛选
        if ("critical".equals(alertLevel)) {
            queryWrapper.apply("""
                (COALESCE(quantity, 0) - COALESCE(locked_quantity, 0) - COALESCE(reserved_quantity, 0) - COALESCE(defective_quantity, 0))
                <= (LEAST(COALESCE(warning_threshold, 0), {0}) * 0.5)
                """, globalThreshold);
        } else if ("warning".equals(alertLevel)) {
            queryWrapper.apply("""
                (COALESCE(quantity, 0) - COALESCE(locked_quantity, 0) - COALESCE(reserved_quantity, 0) - COALESCE(defective_quantity, 0))
                > (LEAST(COALESCE(warning_threshold, 0), {0}) * 0.5)
                """, globalThreshold);
        }

        queryWrapper.orderByAsc(Stock::getQuantity);

        IPage<Stock> pageResult = stockMapper.selectPage(pageParam, queryWrapper);
        return pageResult.convert(stock -> toAlertItem(stock, globalThreshold));
    }

    @Override
    public AlertSummary getSummary(Long tenantId) {
        StockAlertConfig config = getOrCreateConfig(tenantId);
        int globalThreshold = config.getGlobalThreshold() != null ? config.getGlobalThreshold() : DEFAULT_THRESHOLD;

        long critical = stockMapper.selectCount(new LambdaQueryWrapper<Stock>()
                .apply("""
                    (COALESCE(quantity, 0) - COALESCE(locked_quantity, 0) - COALESCE(reserved_quantity, 0) - COALESCE(defective_quantity, 0))
                    <= (LEAST(COALESCE(warning_threshold, 0), {0}) * 0.5)
                    """, globalThreshold));

        long warning = stockMapper.selectCount(new LambdaQueryWrapper<Stock>()
                .apply("""
                    (COALESCE(quantity, 0) - COALESCE(locked_quantity, 0) - COALESCE(reserved_quantity, 0) - COALESCE(defective_quantity, 0))
                    > (LEAST(COALESCE(warning_threshold, 0), {0}) * 0.5)
                    """, globalThreshold)
                .apply("""
                    (COALESCE(quantity, 0) - COALESCE(locked_quantity, 0) - COALESCE(reserved_quantity, 0) - COALESCE(defective_quantity, 0))
                    <= LEAST(COALESCE(warning_threshold, 0), {0})
                    """, globalThreshold));

        return AlertSummary.builder()
                .critical((int) critical)
                .warning((int) warning)
                .total((int) (critical + warning))
                .globalThreshold(globalThreshold)
                .build();
    }

    @Override
    public AlertTriggerResult triggerAlerts(Long tenantId) {
        StockAlertConfig config = getOrCreateConfig(tenantId);

        if (!Boolean.TRUE.equals(config.getEnabled())) {
            return AlertTriggerResult.builder().checked(0).triggered(0).alerts(List.of()).build();
        }

        // 获取所有预警项
        IPage<AlertItem> alertsPage = listAlerts(tenantId, null, null, null, 1, 100);
        List<AlertItem> alerts = alertsPage.getRecords();
        int checked = (int) alertsPage.getTotal();

        if (checked == 0) {
            return AlertTriggerResult.builder().checked(0).triggered(0).alerts(List.of()).build();
        }

        // 发送通知
        int notified = sendNotifications(tenantId, alerts);

        log.info("库存预警已触发: tenantId={}, checked={}, notified={}", tenantId, checked, notified);
        return AlertTriggerResult.builder()
                .checked(checked)
                .triggered(notified)
                .alerts(alerts.stream().limit(20).toList())
                .build();
    }

    // ==================== 预警设置 ====================

    @Override
    public AlertSettings getSettings(Long tenantId) {
        StockAlertConfig config = getOrCreateConfig(tenantId);
        List<String> channels = parseChannels(config.getAlertChannels());

        return AlertSettings.builder()
                .globalThreshold(config.getGlobalThreshold() != null ? config.getGlobalThreshold() : DEFAULT_THRESHOLD)
                .enabled(config.getEnabled() != null ? config.getEnabled() : true)
                .autoCheckEnabled(config.getAutoCheckEnabled() != null ? config.getAutoCheckEnabled() : false)
                .checkIntervalMinutes(config.getCheckIntervalMinutes() != null ? config.getCheckIntervalMinutes() : 60)
                .notifyOnlyWhenCritical(config.getNotifyOnlyWhenCritical() != null ? config.getNotifyOnlyWhenCritical() : false)
                .alertChannels(channels)
                .tenantId(tenantId)
                .build();
    }

    @Override
    @Transactional
    public StockAlertConfig updateSettings(Long tenantId, AlertSettings settings) {
        StockAlertConfig config = getOrCreateConfig(tenantId);

        if (settings.getGlobalThreshold() != null) config.setGlobalThreshold(settings.getGlobalThreshold());
        if (settings.getEnabled() != null) config.setEnabled(settings.getEnabled());
        if (settings.getAutoCheckEnabled() != null) config.setAutoCheckEnabled(settings.getAutoCheckEnabled());
        if (settings.getCheckIntervalMinutes() != null) config.setCheckIntervalMinutes(settings.getCheckIntervalMinutes());
        if (settings.getNotifyOnlyWhenCritical() != null) config.setNotifyOnlyWhenCritical(settings.getNotifyOnlyWhenCritical());
        if (settings.getAlertChannels() != null) {
            config.setAlertChannels(JSON.toJSONString(settings.getAlertChannels()));
        }
        config.setUpdateTime(LocalDateTime.now());

        alertConfigMapper.updateById(config);
        log.info("预警设置已更新: tenantId={}", tenantId);
        return config;
    }

    // ==================== 通知配置 ====================

    @Override
    public AlertNotification getNotifications(Long tenantId) {
        StockAlertNotification notification = getOrCreateNotification(tenantId);

        return AlertNotification.builder()
                .email(AlertNotification.EmailConfig.builder()
                        .enabled(notification.getEmailEnabled())
                        .recipients(parseRecipients(notification.getEmailRecipients()))
                        .build())
                .dingtalk(AlertNotification.DingtalkConfig.builder()
                        .enabled(notification.getDingtalkEnabled())
                        .webhookUrl(notification.getDingtalkWebhookUrl())
                        .secret(notification.getDingtalkSecret())
                        .atMobiles(parseRecipients(notification.getDingtalkAtMobiles()))
                        .build())
                .tenantId(tenantId)
                .build();
    }

    @Override
    @Transactional
    public StockAlertNotification updateNotifications(Long tenantId, AlertNotification notification) {
        StockAlertNotification record = getOrCreateNotification(tenantId);

        if (notification.getEmail() != null) {
            if (notification.getEmail().getEnabled() != null) record.setEmailEnabled(notification.getEmail().getEnabled());
            if (notification.getEmail().getRecipients() != null) record.setEmailRecipients(String.join(",", notification.getEmail().getRecipients()));
        }
        if (notification.getDingtalk() != null) {
            if (notification.getDingtalk().getEnabled() != null) record.setDingtalkEnabled(notification.getDingtalk().getEnabled());
            if (notification.getDingtalk().getWebhookUrl() != null) record.setDingtalkWebhookUrl(notification.getDingtalk().getWebhookUrl());
            if (notification.getDingtalk().getSecret() != null) record.setDingtalkSecret(notification.getDingtalk().getSecret());
            if (notification.getDingtalk().getAtMobiles() != null) record.setDingtalkAtMobiles(String.join(",", notification.getDingtalk().getAtMobiles()));
        }
        record.setUpdateTime(LocalDateTime.now());

        alertNotificationMapper.updateById(record);
        log.info("预警通知配置已更新: tenantId={}", tenantId);
        return record;
    }

    // ==================== 私有辅助方法 ====================

    private int sendNotifications(Long tenantId, List<AlertItem> alerts) {
        StockAlertConfig config = getOrCreateConfig(tenantId);
        List<String> channels = parseChannels(config.getAlertChannels());
        int notified = 0;

        for (String channel : channels) {
            switch (channel) {
                case "system" -> {
                    log.warn("库存预警通知[system]: tenantId={}, count={}", tenantId, alerts.size());
                    notified++;
                }
                case "email" -> {
                    StockAlertNotification notification = getOrCreateNotification(tenantId);
                    if (Boolean.TRUE.equals(notification.getEmailEnabled())) {
                        log.info("库存预警邮件通知[模拟]: tenantId={}, recipients={}", tenantId, notification.getEmailRecipients());
                        notified++;
                    }
                }
                case "dingtalk" -> {
                    StockAlertNotification notification = getOrCreateNotification(tenantId);
                    if (Boolean.TRUE.equals(notification.getDingtalkEnabled())) {
                        log.info("库存预警钉钉通知[模拟]: tenantId={}, webhook={}", tenantId, notification.getDingtalkWebhookUrl());
                        notified++;
                    }
                }
            }
        }
        return notified;
    }

    private AlertItem toAlertItem(Stock stock, int globalThreshold) {
        Warehouse warehouse = warehouseMapper.selectById(stock.getWarehouseId());
        int available = stock.getAvailableQuantity();
        int threshold = Math.min(
                stock.getWarningThreshold() != null ? stock.getWarningThreshold() : 0,
                globalThreshold
        );

        return AlertItem.builder()
                .id(stock.getId())
                .skuCode(stock.getSkuCode())
                .skuName(stock.getSkuName())
                .warehouseId(stock.getWarehouseId())
                .warehouseName(warehouse != null ? warehouse.getName() : null)
                .quantity(stock.getQuantity())
                .lockedQuantity(stock.getLockedQuantity())
                .reservedQuantity(stock.getReservedQuantity())
                .defectiveQuantity(stock.getDefectiveQuantity())
                .availableQuantity(available)
                .warningThreshold(threshold)
                .alertLevel(available <= threshold * 0.5 ? "critical" : "warning")
                .shortageCount(Math.max(0, threshold - available))
                .build();
    }

    private StockAlertConfig getOrCreateConfig(Long tenantId) {
        StockAlertConfig config = alertConfigMapper.findByTenantId(tenantId);
        if (config == null) {
            config = StockAlertConfig.builder()
                    .tenantId(tenantId)
                    .globalThreshold(DEFAULT_THRESHOLD)
                    .enabled(true)
                    .autoCheckEnabled(false)
                    .checkIntervalMinutes(60)
                    .notifyOnlyWhenCritical(false)
                    .alertChannels("[\"system\"]")
                    .createTime(LocalDateTime.now())
                    .updateTime(LocalDateTime.now())
                    .build();
            alertConfigMapper.insert(config);
        }
        return config;
    }

    private StockAlertNotification getOrCreateNotification(Long tenantId) {
        StockAlertNotification notification = alertNotificationMapper.findByTenantId(tenantId);
        if (notification == null) {
            notification = StockAlertNotification.builder()
                    .tenantId(tenantId)
                    .emailEnabled(false)
                    .dingtalkEnabled(false)
                    .createTime(LocalDateTime.now())
                    .updateTime(LocalDateTime.now())
                    .build();
            alertNotificationMapper.insert(notification);
        }
        return notification;
    }

    private List<String> parseChannels(String json) {
        if (json == null || json.isEmpty()) return List.of("system");
        try {
            return JSON.parseArray(json, String.class);
        } catch (Exception e) {
            return List.of("system");
        }
    }

    private List<String> parseRecipients(String value) {
        if (value == null || value.isEmpty()) return new ArrayList<>();
        return new ArrayList<>(Arrays.asList(value.split(",")));
    }
}
