package com.aps.service.impl;

import com.aps.common.util.EnhancedJsonConversionUtil;
import com.aps.entity.StrategyRule;
import com.aps.entity.StrategyScheduling;
import com.aps.entity.UserStrategyRule;
import com.aps.mapper.UserStrategyRuleMapper;
import com.aps.service.StrategyRuleService;
import com.aps.service.UserStrategyRuleService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

@Service
public class UserStrategyRuleServiceImpl extends ServiceImpl<UserStrategyRuleMapper, UserStrategyRule> implements UserStrategyRuleService {

    @Autowired
    private StrategyRuleService strategyRuleService;

    private final ObjectMapper objectMapper = new ObjectMapper()
            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

    @Override
    public Map<String, Object> getEffectiveRule(Map<String, Object> params) {
        Long userId = getLong(params, "userId");
        String source = getSource(params);
        String ruleId = getRuleId(params);
        // /effective 当前用于恢复用户上次保存的策略，前端旧的 id=USER:xxx 不再作为指定查询条件。
        // 排产入口如果确实要指定某条用户策略，仍可显式传 userRuleId。
        String userRuleId = getString(params, "userRuleId");
        Long baseRuleId = resolveBaseRuleId(params, source, ruleId);

        UserStrategyRule selectedUserRule = findUserRuleById(userRuleId, userId);
        if (selectedUserRule != null) {
            return buildUserResult(selectedUserRule);
        }

        UserStrategyRule userRule = findUserRule(userId, baseRuleId);
        if (userRule != null) {
            return buildUserResult(userRule);
        }

        StrategyRule globalRule = findGlobalRule(baseRuleId);
        return buildGlobalResult(globalRule);
    }

    @Override
    public Map<String, Object> saveUserRule(Map<String, Object> params) {
        Long userId = getLong(params, "userId");
        if (userId == null) {
            throw new RuntimeException("userId不能为空");
        }

        String source = getSource(params);
        String ruleId = getRuleId(params);
        String userRuleId = "USER".equalsIgnoreCase(source) ? ruleId : getString(params, "userRuleId");
        Long baseRuleId = resolveBaseRuleId(params, source, ruleId);
        UserStrategyRule userRule = findUserRuleById(userRuleId, userId);
        if (baseRuleId == null && userRule != null) {
            baseRuleId = userRule.getBaseRuleId();
        }
        if (baseRuleId == null) {
            userRule = findUserRule(userId, null);
            if (userRule != null) {
                baseRuleId = userRule.getBaseRuleId();
            }
        }

        if (baseRuleId == null) {
            throw new RuntimeException("baseRuleId不能为空");
        }

        if (userRule == null || !baseRuleId.equals(userRule.getBaseRuleId())) {
            userRule = findUserRule(userId, baseRuleId);
        }
        StrategyRule globalRule = strategyRuleService.getById(baseRuleId);
        if (globalRule == null || isDeleted(globalRule.getIsDeleted())) {
            throw new RuntimeException("baseRuleId不存在");
        }

        boolean newUserRule = userRule == null;
        LocalDateTime now = LocalDateTime.now();
        if (userRule == null) {
            userRule = new UserStrategyRule();
            userRule.setId(UUID.randomUUID().toString());
            userRule.setCreationtime(now);
            userRule.setCreatoruserid(userId);
            userRule.setIsdeleted(0L);
            userRule.setUserid(userId);
            userRule.setSourceType("USER");
        }

        userRule.setLastmodificationtime(now);
        userRule.setLastmodifieruserid(userId);
        userRule.setBaseRuleId(baseRuleId);
        userRule.setSceneId(null);
        userRule.setName(resolveName(params, globalRule, newUserRule));
        userRule.setForwardScheduling(toForwardSchedulingJson(params, userRule, globalRule));
        userRule.setKpiConfig(toJson(params.get("kpiConfig"), null));
        userRule.setIsDefault(1L);

        boolean saved = this.saveOrUpdate(userRule);
        if (!saved) {
            throw new RuntimeException("UserStrategyRule save failed");
        }
        return buildUserResult(userRule);
    }

    @Override
    public boolean resetUserRule(Map<String, Object> params) {
        Long userId = getLong(params, "userId");
        String source = getSource(params);
        String ruleId = getRuleId(params);
        String userRuleId = "USER".equalsIgnoreCase(source) ? ruleId : getString(params, "userRuleId");
        Long baseRuleId = resolveBaseRuleId(params, source, ruleId);
        UserStrategyRule userRule = null;

        if (StringUtils.hasText(userRuleId)) {
            userRule = this.getById(userRuleId);
            if (userRule == null || userId == null || !userId.equals(userRule.getUserid())) {
                return false;
            }
            return deleteUserRule(userRule, userId, LocalDateTime.now());
        }

        if (userId == null) {
            return false;
        }

        List<UserStrategyRule> rules = findUserRules(userId, baseRuleId);
        if (rules == null || rules.isEmpty()) {
            return false;
        }

        LocalDateTime now = LocalDateTime.now();
        boolean success = true;
        for (UserStrategyRule rule : rules) {
            success = deleteUserRule(rule, userId, now) && success;
        }
        return success;
    }

    @Override
    public List<Map<String, Object>> getGlobalRules() {
        return getGlobalRuleOptions();
    }

    @Override
    public List<Map<String, Object>> getStrategyOptions(Long userId) {
        return getMergedStrategyOptions(userId);
    }

    private List<Map<String, Object>> getMergedStrategyOptions(Long userId) {
        List<StrategyRule> globalRules = findGlobalRules();
        Map<Long, UserStrategyRule> userRuleMap = findUserRuleMapByBaseRuleId(userId);
        List<Map<String, Object>> result = new ArrayList<>();
        for (StrategyRule globalRule : globalRules) {
            UserStrategyRule userRule = userRuleMap.get(globalRule.getId());
            result.add(userRule == null ? buildGlobalOption(globalRule) : buildUserOption(userRule, globalRule));
        }
        return result;
    }

    private List<Map<String, Object>> getGlobalRuleOptions() {
        List<StrategyRule> rules = findGlobalRules();
        List<Map<String, Object>> result = new ArrayList<>();
        for (StrategyRule rule : rules) {
            result.add(buildGlobalOption(rule));
        }
        return result;
    }

    private Map<Long, UserStrategyRule> findUserRuleMapByBaseRuleId(Long userId) {
        List<UserStrategyRule> rules = findUserRules(userId, null);
        Map<Long, UserStrategyRule> result = new HashMap<>();
        if (rules == null || rules.isEmpty()) {
            return result;
        }
        rules.sort(Comparator
                .comparing((UserStrategyRule rule) -> rule.getIsDefault() == null ? 0L : rule.getIsDefault(), Comparator.reverseOrder())
                .thenComparing(rule -> rule.getLastmodificationtime() == null ? LocalDateTime.MIN : rule.getLastmodificationtime(), Comparator.reverseOrder()));
        for (UserStrategyRule rule : rules) {
            if (rule.getBaseRuleId() != null && !result.containsKey(rule.getBaseRuleId())) {
                result.put(rule.getBaseRuleId(), rule);
            }
        }
        return result;
    }

    private List<StrategyRule> findGlobalRules() {
        return strategyRuleService.lambdaQuery()
                .eq(StrategyRule::getIsDeleted, 0)
                .list();
    }

    private Map<String, Object> buildGlobalOption(StrategyRule rule) {
        Map<String, Object> item = new HashMap<>();
        item.put("id", rule.getId());
        item.put("source", "GLOBAL");
        item.put("userRuleId", null);
        item.put("baseRuleId", rule.getId());
        item.put("name", rule.getName());
        item.put("globalRuleId", rule.getId());
        item.put("globalRuleName", rule.getName());
        item.put("forwardScheduling", parseForwardScheduling(rule.getForwardScheduling()));
        item.put("isjit", toFrontendIsJit(extractIsJit(rule.getForwardScheduling(), false)));
        item.put("isOptimizer", extractIsOptimizer(rule.getForwardScheduling(), true));
        return item;
    }

    private Map<String, Object> buildUserOption(UserStrategyRule rule, StrategyRule globalRule) {
        Map<String, Object> item = new HashMap<>();
        item.put("id", globalRule.getId());
        item.put("source", "USER");
        item.put("userRuleId", rule.getId());
        item.put("baseRuleId", rule.getBaseRuleId());
        item.put("name", rule.getName());
        item.put("isDefault", rule.getIsDefault());
        item.put("globalRuleId", globalRule.getId());
        item.put("globalRuleName", globalRule.getName());
        item.put("forwardScheduling", parseForwardScheduling(rule.getForwardScheduling()));
        item.put("isjit", toFrontendIsJit(extractIsJit(rule.getForwardScheduling(), false)));
        item.put("isOptimizer", extractIsOptimizer(rule.getForwardScheduling(), true));
        return item;
    }

    @Override
    public List<StrategyScheduling> getEffectiveForwardScheduling(Long userId, Long baseRuleId, String sceneId) {
        return getEffectiveForwardScheduling(userId, baseRuleId, sceneId, null);
    }

    @Override
    public List<StrategyScheduling> getEffectiveForwardScheduling(Long userId, Long baseRuleId, String sceneId, String userRuleId) {
        // 1. 如果 /lanuch/schedule 明确带了 userRuleId，就优先使用这条刚保存的用户策略。
        UserStrategyRule selectedUserRule = findUserRuleByIdOptionalUser(userRuleId, userId);
        if (selectedUserRule != null) {
            return parseForwardScheduling(selectedUserRule.getForwardScheduling());
        }

        // 2. 没有明确 userRuleId 时，按 userId/baseRuleId 找当前用户可用的策略。
        UserStrategyRule userRule = findUserRule(userId, baseRuleId);
        if (userRule != null) {
            return parseForwardScheduling(userRule.getForwardScheduling());
        }
        // 3. 用户没有自定义策略时，回退到基础/全局策略，保证排产还能继续跑。
        StrategyRule globalRule = findGlobalRule(baseRuleId);
        return globalRule == null ? new ArrayList<StrategyScheduling>() : parseForwardScheduling(globalRule.getForwardScheduling());
    }

    private UserStrategyRule findUserRule(Long userId, Long baseRuleId) {
        List<UserStrategyRule> rules = findUserRules(userId, baseRuleId);
        if (rules == null || rules.isEmpty()) {
            return null;
        }

        // 同一个用户可能有多条策略：默认策略优先，其次取最近修改的一条。
        rules.sort(Comparator
                .comparing((UserStrategyRule rule) -> rule.getIsDefault() == null ? 0L : rule.getIsDefault(), Comparator.reverseOrder())
                .thenComparing(rule -> rule.getLastmodificationtime() == null ? LocalDateTime.MIN : rule.getLastmodificationtime(), Comparator.reverseOrder()));
        return rules.get(0);
    }

    private UserStrategyRule findUserRuleById(String userRuleId, Long userId) {
        if (!StringUtils.hasText(userRuleId) || userId == null) {
            return null;
        }

        UserStrategyRule rule = this.getById(userRuleId);
        if (rule == null || !userId.equals(rule.getUserid()) || isDeleted(rule.getIsdeleted())) {
            return null;
        }
        return rule;
    }

    private UserStrategyRule findUserRuleByIdOptionalUser(String userRuleId, Long userId) {
        if (!StringUtils.hasText(userRuleId)) {
            return null;
        }

        UserStrategyRule rule = this.getById(userRuleId);
        if (rule == null || isDeleted(rule.getIsdeleted())) {
            return null;
        }
        // 如果能解析到 userId，则校验策略归属，避免误用其他用户的策略。
        if (userId != null && !userId.equals(rule.getUserid())) {
            return null;
        }
        return rule;
    }

    private List<UserStrategyRule> findUserRules(Long userId, Long baseRuleId) {
        if (userId == null) {
            return null;
        }

        return this.lambdaQuery()
                .eq(UserStrategyRule::getUserid, userId)
                .eq(UserStrategyRule::getIsdeleted, 0)
                .eq(baseRuleId != null, UserStrategyRule::getBaseRuleId, baseRuleId)
                .list();
    }

    private StrategyRule findGlobalRule(Long baseRuleId) {
        if (baseRuleId != null) {
            StrategyRule rule = strategyRuleService.getById(baseRuleId);
            if (rule != null && !isDeleted(rule.getIsDeleted())) {
                return rule;
            }
        }

        List<StrategyRule> rules = strategyRuleService.lambdaQuery()
                .eq(StrategyRule::getIsDeleted, 0)
                .list();
        return rules == null || rules.isEmpty() ? null : rules.get(0);
    }

    private Map<String, Object> buildUserResult(UserStrategyRule rule) {
        Map<String, Object> result = new HashMap<>();
        result.put("id", rule.getBaseRuleId());
        result.put("source", "USER");
        result.put("userRuleId", rule.getId());
        result.put("baseRuleId", rule.getBaseRuleId());
        result.put("sceneId", null);
        result.put("name", rule.getName());
        result.put("forwardScheduling", parseForwardScheduling(rule.getForwardScheduling()));
        result.put("kpiConfig", parseJson(rule.getKpiConfig()));
        result.put("isjit", toFrontendIsJit(extractIsJit(rule.getForwardScheduling(), false)));
        result.put("isOptimizer", extractIsOptimizer(rule.getForwardScheduling(), true));
        return result;
    }

    private Map<String, Object> buildGlobalResult(StrategyRule rule) {
        Map<String, Object> result = new HashMap<>();
        result.put("id", rule == null ? null : rule.getId());
        result.put("source", "GLOBAL");
        result.put("userRuleId", null);
        result.put("baseRuleId", rule == null ? null : rule.getId());
        result.put("sceneId", null);
        result.put("name", rule == null ? null : rule.getName());
        result.put("forwardScheduling", rule == null ? new ArrayList<StrategyScheduling>() : parseForwardScheduling(rule.getForwardScheduling()));
        result.put("kpiConfig", new ArrayList<>());
        result.put("isjit", toFrontendIsJit(rule != null && extractIsJit(rule.getForwardScheduling(), false)));
        result.put("isOptimizer", rule == null || extractIsOptimizer(rule.getForwardScheduling(), true));
        return result;
    }

    private boolean deleteUserRule(UserStrategyRule userRule, Long userId, LocalDateTime now) {
        userRule.setIsdeleted(1L);
        userRule.setDeletiontime(now);
        userRule.setDeleteruserid(userId);
        userRule.setLastmodificationtime(now);
        userRule.setLastmodifieruserid(userId);
        return this.updateById(userRule);
    }

    private List<StrategyScheduling> parseForwardScheduling(String json) {
        if (!StringUtils.hasText(json)) {
            return new ArrayList<>();
        }
        try {
            JsonNode root = objectMapper.readTree(json);
            if (root == null || root.isNull()) {
                return new ArrayList<>();
            }
            // 新版用户策略保存的是数组；老的全局策略可能保存为 {kpiList, ruleList}。
            JsonNode schedulingNode = root.isArray() ? root : root.path("forwardScheduling");
            if (!schedulingNode.isArray()) {
                schedulingNode = root.path("ruleList");
            }
            if (!schedulingNode.isArray()) {
                return new ArrayList<>();
            }
            return EnhancedJsonConversionUtil.convertJsonToEntities(schedulingNode.toString(), null, StrategyScheduling.class);
        } catch (Exception e) {
            return new ArrayList<>();
        }
    }

    private Object parseJson(String json) {
        if (!StringUtils.hasText(json)) {
            return new ArrayList<>();
        }
        try {
            return objectMapper.readValue(json, Object.class);
        } catch (Exception e) {
            return new ArrayList<>();
        }
    }

    private String toForwardSchedulingJson(Map<String, Object> params, UserStrategyRule userRule, StrategyRule globalRule) {
        Object forwardScheduling = params.get("forwardScheduling");
        String defaultValue = globalRule == null ? null : globalRule.getForwardScheduling();
        Object rawIsJit = firstValue(params, "isjit", "isJit", "jit");
        Object rawIsOptimizer = firstValue(params, "isOptimizer");

        Boolean isJit = rawIsJit == null ? extractIsJitObject(userRule.getForwardScheduling()) : !asBoolean(rawIsJit, true);
        if (isJit == null) {
            isJit = extractIsJitObject(defaultValue);
        }
        Boolean isOptimizer = rawIsOptimizer == null ? extractIsOptimizerObject(userRule.getForwardScheduling()) : asBoolean(rawIsOptimizer, true);
        if (isOptimizer == null) {
            isOptimizer = extractIsOptimizerObject(defaultValue);
        }
        if (isOptimizer == null) {
            isOptimizer = true;
        }

        Map<String, Object> payload = new LinkedHashMap<>();
        payload.put("forwardScheduling", forwardScheduling == null ? parseForwardScheduling(defaultValue) : forwardScheduling);
        payload.put("isjit", isJit == null ? false : isJit);
        payload.put("isOptimizer", isOptimizer);
        return toJson(payload, defaultValue);
    }

    private String toJson(Object value, String defaultValue) {
        if (value == null) {
            return defaultValue;
        }
        if (value instanceof String) {
            return (String) value;
        }
        try {
            return objectMapper.writeValueAsString(value);
        } catch (Exception e) {
            throw new RuntimeException("JSON转换失败", e);
        }
    }

    private Boolean extractIsJitObject(String json) {
        if (!StringUtils.hasText(json)) {
            return null;
        }
        try {
            JsonNode root = objectMapper.readTree(json);
            JsonNode value = firstJsonValue(root, "isjit", "isJit", "jit");
            return value == null ? null : asBoolean(value.isValueNode() ? value.asText() : value.toString(), false);
        } catch (Exception e) {
            return null;
        }
    }

    private boolean extractIsJit(String json, boolean defaultValue) {
        Boolean value = extractIsJitObject(json);
        return value == null ? defaultValue : value;
    }

    private boolean toFrontendIsJit(boolean backendIsJit) {
        return !backendIsJit;
    }

    private Boolean extractIsOptimizerObject(String json) {
        if (!StringUtils.hasText(json)) {
            return null;
        }
        try {
            JsonNode root = objectMapper.readTree(json);
            JsonNode value = firstJsonValue(root, "isOptimizer");
            return value == null ? null : asBoolean(value.isValueNode() ? value.asText() : value.toString(), true);
        } catch (Exception e) {
            return null;
        }
    }

    private boolean extractIsOptimizer(String json, boolean defaultValue) {
        Boolean value = extractIsOptimizerObject(json);
        return value == null ? defaultValue : value;
    }

    private JsonNode firstJsonValue(JsonNode root, String... keys) {
        if (root == null || root.isNull()) {
            return null;
        }
        for (String key : keys) {
            JsonNode value = root.get(key);
            if (value != null && !value.isNull()) {
                return value;
            }
        }
        return null;
    }

    private Object firstValue(Map<String, Object> params, String... keys) {
        if (params == null || keys == null) {
            return null;
        }
        for (String key : keys) {
            if (params.containsKey(key)) {
                return params.get(key);
            }
        }
        return null;
    }

    private String resolveName(Map<String, Object> params, StrategyRule globalRule, boolean newUserRule) {
        String name = getString(params, "name");
        if (!StringUtils.hasText(name)) {
            name = getString(params, "referenceName");
        }
        if (StringUtils.hasText(name)) {
            return name;
        }
        if (globalRule == null) {
            return null;
        }
        return globalRule.getName();
    }

    private Long getLong(Map<String, Object> params, String key) {
        if (params == null || params.get(key) == null || !StringUtils.hasText(String.valueOf(params.get(key)))) {
            return null;
        }
        return toLong(String.valueOf(params.get(key)));
    }

    private Long resolveBaseRuleId(Map<String, Object> params, String source, String ruleId) {
        if ("GLOBAL".equalsIgnoreCase(source)) {
            return toLong(ruleId);
        }
        Long strategyRuleId = getLongIfNumeric(params, "ruleId");
        if (strategyRuleId != null) {
            return strategyRuleId;
        }
        Long numericId = getLongIfNumeric(params, "id");
        if (numericId != null) {
            return numericId;
        }
        Long baseRuleId = getLong(params, "baseRuleId");
        // 前端保存策略时可能传 referenceId；只有纯数字时才兼容成基础策略 ID，UUID 行引用要忽略。
        if (baseRuleId != null) {
            return baseRuleId;
        }
        Long referenceId = getLongIfNumeric(params, "referenceId");
        return referenceId == null ? getLongIfNumeric(params, "reference") : referenceId;
    }

    private Long getLongIfNumeric(Map<String, Object> params, String key) {
        String value = getString(params, key);
        if (!StringUtils.hasText(value) || !value.matches("\\d+")) {
            return null;
        }
        return toLong(value);
    }

    private String getString(Map<String, Object> params, String key) {
        if (params == null || params.get(key) == null) {
            return null;
        }
        String value = String.valueOf(params.get(key));
        return StringUtils.hasText(value) ? value : null;
    }

    private String getSource(Map<String, Object> params) {
        String source = getString(params, "source");
        if (StringUtils.hasText(source)) {
            return source;
        }

        String id = getString(params, "id");
        int index = id == null ? -1 : id.indexOf(":");
        return index > 0 ? id.substring(0, index) : null;
    }

    private String getRuleId(Map<String, Object> params) {
        String ruleId = getString(params, "ruleId");
        if (StringUtils.hasText(ruleId)) {
            return ruleId;
        }

        String id = getString(params, "id");
        int index = id == null ? -1 : id.indexOf(":");
        return index > -1 && index < id.length() - 1 ? id.substring(index + 1) : null;
    }

    private Long toLong(String value) {
        if (!StringUtils.hasText(value)) {
            return null;
        }
        return Long.valueOf(value);
    }

    private boolean asBoolean(Object value, boolean defaultValue) {
        if (value == null) {
            return defaultValue;
        }
        if (value instanceof Boolean) {
            return (Boolean) value;
        }
        if (value instanceof Number) {
            return ((Number) value).intValue() != 0;
        }
        String text = String.valueOf(value).trim();
        if ("1".equals(text)) {
            return true;
        }
        if ("0".equals(text)) {
            return false;
        }
        return text.isEmpty() ? defaultValue : Boolean.parseBoolean(text);
    }

    private boolean isDeleted(Number value) {
        return value != null && value.longValue() != 0L;
    }
}
