package com.erp.approval.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.erp.approval.dto.*;
import com.erp.approval.entity.AuditRule;
import com.erp.approval.enums.ActionType;
import com.erp.approval.enums.ConditionOperator;
import com.erp.approval.enums.RuleType;
import com.erp.approval.exception.BusinessException;
import com.erp.approval.repository.AuditRuleRepository;
import com.erp.approval.service.AuditLogService;
import com.erp.approval.service.AuditRuleService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import com.erp.common.core.model.PageResponse;

/**
 * 审核规则服务实现
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AuditRuleServiceImpl implements AuditRuleService {
    
    private final AuditRuleRepository auditRuleRepository;
    private final AuditLogService auditLogService;
    private final ObjectMapper objectMapper;
    
    private static final Map<String, List<FieldDefinition>> TYPE_FIELDS = new HashMap<>();
    
    static {
        // 订单审核字段
        TYPE_FIELDS.put("order", Arrays.asList(
            new FieldDefinition("total_amount", "订单金额", "number"),
            new FieldDefinition("platform", "平台", "string"),
            new FieldDefinition("shop_id", "店铺ID", "number"),
            new FieldDefinition("goods_amount", "商品金额", "number"),
            new FieldDefinition("freight", "运费", "number"),
            new FieldDefinition("discount_amount", "优惠金额", "number"),
            new FieldDefinition("order_status", "订单状态", "string"),
            new FieldDefinition("platform_status", "平台状态", "string"),
            new FieldDefinition("warehouse_id", "仓库ID", "number"),
            new FieldDefinition("buyer_nick", "买家昵称", "string"),
            new FieldDefinition("item_count", "商品数量", "number"),
            new FieldDefinition("has_remark", "是否有备注", "boolean"),
            new FieldDefinition("receiver_address", "收货地址", "string")
        ));
        
        // 退款审核字段
        TYPE_FIELDS.put("refund", Arrays.asList(
            new FieldDefinition("refund_amount", "退款金额", "number"),
            new FieldDefinition("refund_reason", "退款原因", "string"),
            new FieldDefinition("platform", "平台", "string"),
            new FieldDefinition("shop_id", "店铺ID", "number"),
            new FieldDefinition("order_id", "原订单ID", "number")
        ));
    }
    
    @Override
    public PageResponse<AuditRuleResponse> listRules(Long tenantId, AuditRuleQueryRequest request) {
        LambdaQueryWrapper<AuditRule> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AuditRule::getTenantId, tenantId);
        queryWrapper.isNull(AuditRule::getDeletedAt);
        
        if (StringUtils.hasText(request.getType())) {
            queryWrapper.eq(AuditRule::getType, request.getType());
        }
        
        if (request.getIsActive() != null) {
            queryWrapper.eq(AuditRule::getIsActive, request.getIsActive());
        }
        
        if (StringUtils.hasText(request.getKeyword())) {
            queryWrapper.like(AuditRule::getName, request.getKeyword());
        }
        
        // 排序
        if ("priority".equals(request.getSortField())) {
            queryWrapper.orderByAsc(AuditRule::getPriority);
        } else if ("created_at".equals(request.getSortField())) {
            queryWrapper.orderByDesc(AuditRule::getCreatedAt);
        } else if ("updated_at".equals(request.getSortField())) {
            queryWrapper.orderByDesc(AuditRule::getUpdatedAt);
        } else if ("name".equals(request.getSortField())) {
            queryWrapper.orderByAsc(AuditRule::getName);
        }
        
        int page = request.getPage() != null ? request.getPage() : 1;
        int pageSize = request.getPageSize() != null ? request.getPageSize() : 20;
        Page<AuditRule> pageResult = auditRuleRepository.selectPage(
            new Page<>(page, pageSize), 
            queryWrapper
        );
        
        List<AuditRuleResponse> list = pageResult.getRecords().stream()
            .map(this::toResponse)
            .collect(Collectors.toList());
        
        return PageResponse.of(list, pageResult.getTotal(), page, pageSize);
    }
    
    @Override
    public Optional<AuditRuleResponse> getRuleById(Long tenantId, Long id) {
        LambdaQueryWrapper<AuditRule> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AuditRule::getId, id)
                   .eq(AuditRule::getTenantId, tenantId)
                   .isNull(AuditRule::getDeletedAt);
        
        AuditRule rule = auditRuleRepository.selectOne(queryWrapper);
        return Optional.ofNullable(rule).map(this::toResponse);
    }
    
    @Override
    @Transactional
    public AuditRuleResponse createRule(Long tenantId, Long userId, AuditRuleCreateRequest request) {
        // 检查名称唯一性
        if (auditRuleRepository.countByTenantIdAndName(tenantId, request.getName()) > 0) {
            throw new BusinessException("规则名称已存在");
        }
        
        // 计算优先级
        Integer priority = request.getPriority();
        if (priority == null) {
            Integer maxPriority = auditRuleRepository.findMaxPriorityByTenantIdAndType(tenantId, request.getType());
            priority = (maxPriority != null ? maxPriority : 0) + 1;
        }
        
        AuditRule rule = new AuditRule();
        rule.setTenantId(tenantId);
        rule.setName(request.getName());
        rule.setType(request.getType());
        rule.setConditions(toJson(request.getConditions()));
        rule.setActions(toJson(request.getActions()));
        rule.setPriority(priority);
        rule.setIsActive(request.getIsActive() != null ? request.getIsActive() : true);
        rule.setDescription(request.getDescription());
        rule.setCreatedBy(userId);
        rule.setUpdatedBy(userId);
        rule.setCreatedAt(LocalDateTime.now());
        rule.setUpdatedAt(LocalDateTime.now());
        
        auditRuleRepository.insert(rule);
        
        // 记录日志
        auditLogService.logCreate("AuditRule", "AuditRule", rule.getId(), toMap(request));
        
        return toResponse(rule);
    }
    
    @Override
    @Transactional
    public Optional<AuditRuleResponse> updateRule(Long tenantId, Long userId, Long id, AuditRuleUpdateRequest request) {
        LambdaQueryWrapper<AuditRule> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AuditRule::getId, id)
                   .eq(AuditRule::getTenantId, tenantId)
                   .isNull(AuditRule::getDeletedAt);
        
        AuditRule rule = auditRuleRepository.selectOne(queryWrapper);
        if (rule == null) {
            return Optional.empty();
        }
        
        // 检查名称唯一性（排除自身）
        if (request.getName() != null && !request.getName().equals(rule.getName())) {
            if (auditRuleRepository.countByTenantIdAndNameExcludeId(tenantId, request.getName(), id) > 0) {
                throw new BusinessException("规则名称已存在");
            }
            rule.setName(request.getName());
        }
        
        if (request.getType() != null) {
            rule.setType(request.getType());
        }
        
        if (request.getConditions() != null) {
            rule.setConditions(toJson(request.getConditions()));
        }
        
        if (request.getActions() != null) {
            rule.setActions(toJson(request.getActions()));
        }
        
        if (request.getPriority() != null) {
            rule.setPriority(request.getPriority());
        }
        
        if (request.getIsActive() != null) {
            rule.setIsActive(request.getIsActive());
        }
        
        if (request.getDescription() != null) {
            rule.setDescription(request.getDescription());
        }
        
        rule.setUpdatedBy(userId);
        rule.setUpdatedAt(LocalDateTime.now());
        
        auditRuleRepository.updateById(rule);
        
        // 记录日志
        auditLogService.logUpdate("AuditRule", "AuditRule", rule.getId(), toMap(rule), toMap(request));
        
        return Optional.of(toResponse(rule));
    }
    
    @Override
    @Transactional
    public boolean deleteRule(Long tenantId, Long id) {
        LambdaQueryWrapper<AuditRule> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AuditRule::getId, id)
                   .eq(AuditRule::getTenantId, tenantId)
                   .isNull(AuditRule::getDeletedAt);
        
        AuditRule rule = auditRuleRepository.selectOne(queryWrapper);
        if (rule == null) {
            return false;
        }
        
        // 软删除
        rule.setDeletedAt(LocalDateTime.now());
        auditRuleRepository.updateById(rule);
        
        // 记录日志
        auditLogService.logDelete("AuditRule", "AuditRule", rule.getId(), toMap(rule));
        
        return true;
    }
    
    @Override
    @Transactional
    public int batchDeleteRules(Long tenantId, List<Long> ids) {
        if (ids == null || ids.isEmpty()) {
            return 0;
        }
        
        LambdaQueryWrapper<AuditRule> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AuditRule::getTenantId, tenantId)
                   .in(AuditRule::getId, ids)
                   .isNull(AuditRule::getDeletedAt);
        
        List<AuditRule> rules = auditRuleRepository.selectList(queryWrapper);
        if (rules.isEmpty()) {
            return 0;
        }
        
        LocalDateTime now = LocalDateTime.now();
        for (AuditRule rule : rules) {
            rule.setDeletedAt(now);
            auditRuleRepository.updateById(rule);
        }
        
        return rules.size();
    }
    
    @Override
    @Transactional
    public int batchToggleActive(Long tenantId, Long userId, List<Long> ids, Boolean isActive) {
        if (ids == null || ids.isEmpty()) {
            return 0;
        }
        
        LambdaQueryWrapper<AuditRule> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AuditRule::getTenantId, tenantId)
                   .in(AuditRule::getId, ids)
                   .isNull(AuditRule::getDeletedAt);
        
        List<AuditRule> rules = auditRuleRepository.selectList(queryWrapper);
        if (rules.isEmpty()) {
            return 0;
        }
        
        for (AuditRule rule : rules) {
            rule.setIsActive(isActive);
            rule.setUpdatedBy(userId);
            rule.setUpdatedAt(LocalDateTime.now());
            auditRuleRepository.updateById(rule);
        }
        
        return rules.size();
    }
    
    @Override
    @Transactional
    public void reorderRules(Long tenantId, Long userId, AuditRuleReorderRequest request) {
        List<AuditRuleReorderRequest.RuleOrderItem> orders = request.getOrders();
        if (orders == null || orders.isEmpty()) {
            return;
        }
        
        for (AuditRuleReorderRequest.RuleOrderItem order : orders) {
            LambdaQueryWrapper<AuditRule> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(AuditRule::getId, order.getId())
                       .eq(AuditRule::getTenantId, tenantId)
                       .isNull(AuditRule::getDeletedAt);
            
            AuditRule rule = auditRuleRepository.selectOne(queryWrapper);
            if (rule != null) {
                rule.setPriority(order.getPriority());
                rule.setUpdatedBy(userId);
                rule.setUpdatedAt(LocalDateTime.now());
                auditRuleRepository.updateById(rule);
            }
        }
    }
    
    @Override
    @Transactional
    public Optional<AuditRuleResponse> duplicateRule(Long tenantId, Long userId, Long id) {
        LambdaQueryWrapper<AuditRule> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AuditRule::getId, id)
                   .eq(AuditRule::getTenantId, tenantId)
                   .isNull(AuditRule::getDeletedAt);
        
        AuditRule original = auditRuleRepository.selectOne(queryWrapper);
        if (original == null) {
            return Optional.empty();
        }
        
        // 获取新优先级
        Integer maxPriority = auditRuleRepository.findMaxPriorityByTenantIdAndType(tenantId, original.getType());
        Integer newPriority = (maxPriority != null ? maxPriority : 0) + 1;
        
        AuditRule copy = new AuditRule();
        copy.setTenantId(tenantId);
        copy.setName(original.getName() + " (副本)");
        copy.setType(original.getType());
        copy.setConditions(original.getConditions());
        copy.setActions(original.getActions());
        copy.setPriority(newPriority);
        copy.setIsActive(false);
        copy.setDescription(original.getDescription());
        copy.setCreatedBy(userId);
        copy.setUpdatedBy(userId);
        copy.setCreatedAt(LocalDateTime.now());
        copy.setUpdatedAt(LocalDateTime.now());
        
        auditRuleRepository.insert(copy);
        
        // 记录日志
        auditLogService.logCreate("AuditRule", "AuditRule", copy.getId(), toMap(copy));
        
        return Optional.of(toResponse(copy));
    }
    
    @Override
    public Map<String, Object> testRule(Long tenantId, Long id, AuditRuleTestRequest request) {
        LambdaQueryWrapper<AuditRule> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AuditRule::getId, id)
                   .eq(AuditRule::getTenantId, tenantId)
                   .isNull(AuditRule::getDeletedAt);
        
        AuditRule rule = auditRuleRepository.selectOne(queryWrapper);
        if (rule == null) {
            throw new BusinessException("规则不存在");
        }
        
        Map<String, Object> testData = request.getData();
        boolean matched = matchConditions(rule, testData);
        
        List<ConditionDTO> conditions = parseConditions(rule.getConditions());
        List<ActionDTO> actions = matched ? parseActions(rule.getActions()) : null;
        
        Map<String, Object> result = new HashMap<>();
        result.put("rule_id", rule.getId());
        result.put("rule_name", rule.getName());
        result.put("matched", matched);
        result.put("rule_conditions", conditions);
        result.put("rule_actions", actions);
        result.put("test_data", testData);
        result.put("condition_details", explainConditions(conditions, testData));
        
        return result;
    }
    
    @Override
    public Map<String, Object> simulateRules(Long tenantId, AuditRuleSimulateRequest request) {
        String type = request.getType();
        Map<String, Object> testData = request.getData();
        
        // 获取该租户该类型所有活跃规则（按优先级）
        List<AuditRule> rules = auditRuleRepository.findActiveByTenantIdAndTypeOrderByPriority(tenantId, type);
        
        if (rules.isEmpty()) {
            Map<String, Object> result = new HashMap<>();
            result.put("type", type);
            result.put("matched_rule", null);
            result.put("final_action", null);
            result.put("applied_rules", Collections.emptyList());
            return result;
        }
        
        List<Map<String, Object>> appliedRules = new ArrayList<>();
        Map<String, Object> finalAction = null;
        
        for (AuditRule rule : rules) {
            if (matchConditions(rule, testData)) {
                Map<String, Object> appliedRule = new HashMap<>();
                appliedRule.put("id", rule.getId());
                appliedRule.put("name", rule.getName());
                appliedRule.put("priority", rule.getPriority());
                appliedRule.put("actions", parseActions(rule.getActions()));
                appliedRules.add(appliedRule);
                
                // 取最高优先级（第一个）匹配的规则的动作作为最终动作
                if (finalAction == null) {
                    List<ActionDTO> ruleActions = parseActions(rule.getActions());
                    if (!ruleActions.isEmpty()) {
                        finalAction = new HashMap<>();
                        finalAction.put("type", ruleActions.get(0).getType());
                        finalAction.put("params", ruleActions.get(0).getParams());
                    }
                }
            }
        }
        
        Map<String, Object> result = new HashMap<>();
        result.put("type", type);
        result.put("matched_rule", appliedRules.isEmpty() ? null : appliedRules.get(0));
        result.put("applied_rules", appliedRules);
        result.put("final_action", finalAction);
        result.put("test_data", testData);
        
        return result;
    }
    
    @Override
    public Map<String, Object> getFields(String type) {
        Map<String, Object> result = new HashMap<>();
        result.put("fields", TYPE_FIELDS.getOrDefault(type, TYPE_FIELDS.get("order")));
        result.put("operators", getOperators());
        result.put("action_types", getActionTypes());
        result.put("type_labels", getTypeLabels());
        return result;
    }
    
    @Override
    public Map<String, String> getTypes() {
        return getTypeLabels();
    }
    
    // ==================== 私有方法 ====================
    
    private boolean matchConditions(AuditRule rule, Map<String, Object> data) {
        List<ConditionDTO> conditions = parseConditions(rule.getConditions());
        if (conditions == null || conditions.isEmpty()) {
            return false;
        }
        
        for (ConditionDTO condition : conditions) {
            if (!evaluateCondition(condition, data)) {
                return false;
            }
        }
        
        return true;
    }
    
    private boolean evaluateCondition(ConditionDTO condition, Map<String, Object> data) {
        String field = condition.getField();
        String operator = condition.getOperator();
        Object expectedValue = condition.getValue();
        
        if (!StringUtils.hasText(field) || !data.containsKey(field)) {
            return false;
        }
        
        Object fieldValue = data.get(field);
        
        ConditionOperator op;
        try {
            op = ConditionOperator.fromCode(operator);
        } catch (IllegalArgumentException e) {
            return false;
        }
        
        return switch (op) {
            case EQ -> Objects.equals(fieldValue, expectedValue);
            case NE -> !Objects.equals(fieldValue, expectedValue);
            case GT -> compareNumbers(fieldValue, expectedValue) > 0;
            case GTE -> compareNumbers(fieldValue, expectedValue) >= 0;
            case LT -> compareNumbers(fieldValue, expectedValue) < 0;
            case LTE -> compareNumbers(fieldValue, expectedValue) <= 0;
            case IN -> isInList(fieldValue, expectedValue);
            case NOT_IN -> !isInList(fieldValue, expectedValue);
            case CONTAINS -> isStringContains(fieldValue, expectedValue);
            case NOT_CONTAINS -> !isStringContains(fieldValue, expectedValue);
        };
    }
    
    private int compareNumbers(Object actual, Object expected) {
        if (actual == null || expected == null) {
            return -1;
        }
        double actualNum = toDouble(actual);
        double expectedNum = toDouble(expected);
        return Double.compare(actualNum, expectedNum);
    }
    
    private double toDouble(Object value) {
        if (value instanceof Number) {
            return ((Number) value).doubleValue();
        }
        return Double.parseDouble(String.valueOf(value));
    }
    
    private boolean isInList(Object fieldValue, Object expected) {
        if (!(expected instanceof List)) {
            return false;
        }
        List<?> list = (List<?>) expected;
        for (Object item : list) {
            if (Objects.equals(fieldValue, item)) {
                return true;
            }
        }
        return false;
    }
    
    private boolean isStringContains(Object fieldValue, Object expected) {
        if (!(fieldValue instanceof String)) {
            return false;
        }
        return ((String) fieldValue).contains(String.valueOf(expected));
    }
    
    private List<Map<String, Object>> explainConditions(List<ConditionDTO> conditions, Map<String, Object> data) {
        List<Map<String, Object>> details = new ArrayList<>();
        for (ConditionDTO condition : conditions) {
            Map<String, Object> detail = new HashMap<>();
            detail.put("field", condition.getField());
            detail.put("operator", condition.getOperator());
            detail.put("expected", condition.getValue());
            detail.put("actual", data.get(condition.getField()));
            detail.put("matched", evaluateCondition(condition, data));
            details.add(detail);
        }
        return details;
    }
    
    private AuditRuleResponse toResponse(AuditRule rule) {
        return AuditRuleResponse.builder()
            .id(rule.getId())
            .tenantId(rule.getTenantId())
            .name(rule.getName())
            .type(rule.getType())
            .typeLabel(getTypeLabel(rule.getType()))
            .conditions(parseConditions(rule.getConditions()))
            .actions(parseActions(rule.getActions()))
            .priority(rule.getPriority())
            .isActive(rule.getIsActive())
            .description(rule.getDescription())
            .createdBy(rule.getCreatedBy())
            .updatedBy(rule.getUpdatedBy())
            .createdAt(rule.getCreatedAt())
            .updatedAt(rule.getUpdatedAt())
            .build();
    }
    
    private String getTypeLabel(String type) {
        try {
            return RuleType.fromCode(type).getDescription();
        } catch (Exception e) {
            return type;
        }
    }
    
    private Map<String, String> getTypeLabels() {
        Map<String, String> labels = new HashMap<>();
        for (RuleType type : RuleType.values()) {
            labels.put(type.getCode(), type.getDescription());
        }
        return labels;
    }
    
    private List<Map<String, Object>> getOperators() {
        return Arrays.stream(ConditionOperator.values())
            .map(op -> {
                Map<String, Object> map = new HashMap<>();
                map.put("operator", op.getCode());
                map.put("label", op.getDescription());
                map.put("types", getOperatorTypes(op));
                return map;
            })
            .collect(Collectors.toList());
    }
    
    private List<String> getOperatorTypes(ConditionOperator op) {
        return switch (op) {
            case EQ, NE -> Arrays.asList("string", "number", "boolean");
            case GT, GTE, LT, LTE -> Collections.singletonList("number");
            case IN, NOT_IN -> Arrays.asList("string", "number");
            case CONTAINS, NOT_CONTAINS -> Collections.singletonList("string");
        };
    }
    
    private List<Map<String, Object>> getActionTypes() {
        return Arrays.stream(ActionType.values())
            .map(action -> {
                Map<String, Object> map = new HashMap<>();
                map.put("type", action.getCode());
                map.put("label", action.getDescription());
                return map;
            })
            .collect(Collectors.toList());
    }
    
    private List<ConditionDTO> parseConditions(String json) {
        if (!StringUtils.hasText(json)) {
            return Collections.emptyList();
        }
        try {
            return objectMapper.readValue(json, new TypeReference<List<ConditionDTO>>() {});
        } catch (JsonProcessingException e) {
            log.error("解析条件JSON失败: {}", json, e);
            return Collections.emptyList();
        }
    }
    
    private List<ActionDTO> parseActions(String json) {
        if (!StringUtils.hasText(json)) {
            return Collections.emptyList();
        }
        try {
            return objectMapper.readValue(json, new TypeReference<List<ActionDTO>>() {});
        } catch (JsonProcessingException e) {
            log.error("解析动作JSON失败: {}", json, e);
            return Collections.emptyList();
        }
    }
    
    private String toJson(Object obj) {
        try {
            return objectMapper.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            log.error("序列化对象失败: {}", obj, e);
            return "[]";
        }
    }
    
    private Map<String, Object> toMap(Object obj) {
        return objectMapper.convertValue(obj, new TypeReference<Map<String, Object>>() {});
    }
    
    // 内部类：字段定义
    private static class FieldDefinition {
        final String field;
        final String label;
        final String type;
        
        FieldDefinition(String field, String label, String type) {
            this.field = field;
            this.label = label;
            this.type = type;
        }
        
        public String getField() { return field; }
        public String getLabel() { return label; }
        public String getType() { return type; }
    }
}
