package com.ruoyi.generator.service.front;

import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.springframework.stereotype.Component;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.generator.domain.front.dto.BusinessActionDesign;
import com.ruoyi.generator.domain.front.dto.BusinessActionEffectDesign;
import com.ruoyi.generator.domain.front.dto.BusinessActionRuleDesign;
import com.ruoyi.generator.domain.front.dto.DatabaseColumnDesign;
import com.ruoyi.generator.domain.front.dto.DatabaseDesignResponse;
import com.ruoyi.generator.domain.front.dto.DatabaseTableDesign;

@Component
public class AiBusinessBlueprintValidator
{
    private static final int MAX_ACTIONS = 12;
    private static final int MAX_RULES = 12;
    private static final int MAX_EFFECTS = 12;
    private static final Pattern ACTION_CODE = Pattern.compile("^[a-z][a-z0-9_]{1,63}$");
    private static final Pattern API_PATH = Pattern.compile("^/[A-Za-z0-9_./-]{1,180}$");
    private static final Pattern PARAM_PLACEHOLDER = Pattern.compile("^\\$\\{param\\.([a-z][a-z0-9_]*)}$");
    private static final Pattern NUMBER_LITERAL = Pattern.compile("^-?\\d+(\\.\\d+)?$");

    public void validate(DatabaseDesignResponse response)
    {
        if (response == null || StringUtils.isEmpty(response.getBusinessActions()))
        {
            return;
        }
        if (response.getBusinessActions().size() > MAX_ACTIONS)
        {
            throw new ServiceException("Business action count exceeds limit");
        }
        Set<String> tableNames = collectTableNames(response.getTables());
        Set<String> columnNames = collectColumnNames(response.getTables());
        Set<String> actionCodes = new HashSet<String>();
        for (BusinessActionDesign action : response.getBusinessActions())
        {
            validateAction(action, tableNames, columnNames, actionCodes);
        }
    }

    private void validateAction(BusinessActionDesign action, Set<String> tableNames, Set<String> columnNames, Set<String> actionCodes)
    {
        if (action == null)
        {
            throw new ServiceException("Business action cannot be empty");
        }
        if (StringUtils.isBlank(action.getCode()) || !ACTION_CODE.matcher(action.getCode()).matches())
        {
            throw new ServiceException("Business action code is invalid");
        }
        if (!actionCodes.add(action.getCode()))
        {
            throw new ServiceException("Business action code duplicated: " + action.getCode());
        }
        if (StringUtils.isBlank(action.getName()) || action.getName().length() > 80)
        {
            throw new ServiceException("Business action name is required and cannot exceed 80 characters");
        }
        if (StringUtils.isBlank(action.getOwnerTable()) || !tableNames.contains(action.getOwnerTable()))
        {
            throw new ServiceException("业务动作关联表不存在：" + StringUtils.defaultString(action.getOwnerTable()));
        }
        if (StringUtils.isNotBlank(action.getPath()) && !API_PATH.matcher(action.getPath()).matches())
        {
            throw new ServiceException("Business action API path is invalid");
        }
        validateRequestFields(action, columnNames);
        validateRules(action);
        validateRuleChecks(action, tableNames, columnNames);
        validateEffects(action, tableNames, columnNames);
    }

    private void validateRequestFields(BusinessActionDesign action, Set<String> columnNames)
    {
        if (StringUtils.isEmpty(action.getRequestFields()))
        {
            return;
        }
        for (String field : action.getRequestFields())
        {
            if (StringUtils.isBlank(field) || !columnNames.contains(field))
            {
                throw new ServiceException("Business action request field does not exist: " + StringUtils.defaultString(field));
            }
        }
    }

    private void validateRules(BusinessActionDesign action)
    {
        if (StringUtils.isEmpty(action.getRules()))
        {
            return;
        }
        if (action.getRules().size() > MAX_RULES)
        {
            throw new ServiceException("Business rule count exceeds limit");
        }
        for (String rule : action.getRules())
        {
            if (StringUtils.isBlank(rule) || rule.length() > 200)
            {
                throw new ServiceException("Business rule is required and cannot exceed 200 characters");
            }
        }
    }

    private void validateRuleChecks(BusinessActionDesign action, Set<String> tableNames, Set<String> columnNames)
    {
        if (StringUtils.isEmpty(action.getRuleChecks()))
        {
            return;
        }
        if (action.getRuleChecks().size() > MAX_RULES)
        {
            throw new ServiceException("Business rule check count exceeds limit");
        }
        for (BusinessActionRuleDesign rule : action.getRuleChecks())
        {
            if (rule == null)
            {
                throw new ServiceException("Business rule check cannot be empty");
            }
            String type = StringUtils.defaultString(rule.getType()).toUpperCase(Locale.ENGLISH);
            if (!isSupportedRuleType(type))
            {
                throw new ServiceException("Unsupported business rule type: " + type);
            }
            validateTargetTable(rule.getTargetTable(), tableNames, "Business rule target table does not exist: ");
            validateConditionFields(rule.getConditionFields(), columnNames, "Business rule condition field does not exist: ");
            if (requiresRuleTargetField(type) && StringUtils.isBlank(rule.getTargetField()))
            {
                throw new ServiceException("Business rule target field is required: " + type);
            }
            if (StringUtils.isNotBlank(rule.getTargetField()) && !columnNames.contains(rule.getTargetField()))
            {
                throw new ServiceException("Business rule target field does not exist: " + rule.getTargetField());
            }
            if (requiresExpectedValue(type) && StringUtils.isBlank(rule.getExpectedValue()))
            {
                throw new ServiceException("Business rule expected value is required: " + type);
            }
            if ("FIELD_IN".equals(type) && StringUtils.isEmpty(rule.getExpectedValues()))
            {
                throw new ServiceException("Business rule expected values are required: " + type);
            }
            if ("NUMBER_GTE".equals(type))
            {
                validateNumberExpression(rule.getCompareValue(), columnNames, "Business rule compare value is invalid");
            }
            validateSafeValue(rule.getExpectedValue(), columnNames);
            if (rule.getExpectedValues() != null)
            {
                for (String expectedValue : rule.getExpectedValues())
                {
                    validateSafeValue(expectedValue, columnNames);
                }
            }
            if (StringUtils.defaultString(rule.getMessage()).length() > 200)
            {
                throw new ServiceException("Business rule message cannot exceed 200 characters");
            }
        }
    }

    private void validateEffects(BusinessActionDesign action, Set<String> tableNames, Set<String> columnNames)
    {
        if (StringUtils.isEmpty(action.getEffects()))
        {
            return;
        }
        if (action.getEffects().size() > MAX_EFFECTS)
        {
            throw new ServiceException("Business effect count exceeds limit");
        }
        for (BusinessActionEffectDesign effect : action.getEffects())
        {
            validateEffect(effect, tableNames, columnNames);
        }
    }

    private void validateEffect(BusinessActionEffectDesign effect, Set<String> tableNames, Set<String> columnNames)
    {
        if (effect == null)
        {
            throw new ServiceException("Business effect cannot be empty");
        }
        String type = StringUtils.defaultString(effect.getType()).toUpperCase(Locale.ENGLISH);
        if (StringUtils.isBlank(type) || type.length() > 50)
        {
            throw new ServiceException("Business effect type is required and cannot exceed 50 characters");
        }
        if (!isSupportedEffectType(type))
        {
            throw new ServiceException("Unsupported business effect type: " + type);
        }
        validateTargetTable(effect.getTargetTable(), tableNames, "Business effect target table does not exist: ");
        if (requiresEffectTargetField(type) && StringUtils.isBlank(effect.getTargetField()))
        {
            throw new ServiceException("Business effect target field is required: " + type);
        }
        if (StringUtils.isNotBlank(effect.getTargetField()) && !columnNames.contains(effect.getTargetField()))
        {
            throw new ServiceException("Business effect target field does not exist: " + effect.getTargetField());
        }
        validateEffectConditionFields(effect, columnNames);
        validateEffectValues(effect, columnNames);
        if (requiresEffectValues(type) && (effect.getValues() == null || effect.getValues().isEmpty()))
        {
            throw new ServiceException("Business effect values are required: " + type);
        }
        if ("INCREASE_NUMBER".equals(type) || "DECREASE_NUMBER".equals(type))
        {
            validateNumberExpression(effect.getAmount(), columnNames, "Business effect amount is invalid");
            if (StringUtils.isNotBlank(effect.getMinValue()))
            {
                validateNumberExpression(effect.getMinValue(), columnNames, "Business effect min value is invalid");
            }
        }
        validateSafeValue(effect.getValue(), columnNames);
        if (StringUtils.defaultString(effect.getValue()).length() > 200
                || StringUtils.defaultString(effect.getAmount()).length() > 80
                || StringUtils.defaultString(effect.getMinValue()).length() > 80
                || StringUtils.defaultString(effect.getMessage()).length() > 200
                || StringUtils.defaultString(effect.getDescription()).length() > 200)
        {
            throw new ServiceException("Business effect text cannot exceed limit");
        }
    }

    private void validateEffectConditionFields(BusinessActionEffectDesign effect, Set<String> columnNames)
    {
        if (StringUtils.isEmpty(effect.getConditionFields()))
        {
            return;
        }
        for (String field : effect.getConditionFields())
        {
            if (StringUtils.isBlank(field) || !columnNames.contains(field))
            {
                throw new ServiceException("Business effect condition field does not exist: " + StringUtils.defaultString(field));
            }
        }
    }

    private void validateEffectValues(BusinessActionEffectDesign effect, Set<String> columnNames)
    {
        if (effect.getValues() == null)
        {
            return;
        }
        for (Map.Entry<String, String> entry : effect.getValues().entrySet())
        {
            if (StringUtils.isBlank(entry.getKey()) || !columnNames.contains(entry.getKey()))
            {
                throw new ServiceException("Business effect value field does not exist: " + StringUtils.defaultString(entry.getKey()));
            }
            validateSafeValue(entry.getValue(), columnNames);
        }
    }

    private void validateTargetTable(String targetTable, Set<String> tableNames, String messagePrefix)
    {
        if (StringUtils.isBlank(targetTable) || !tableNames.contains(targetTable))
        {
            throw new ServiceException(messagePrefix + StringUtils.defaultString(targetTable));
        }
    }

    private void validateConditionFields(List<String> conditionFields, Set<String> columnNames, String messagePrefix)
    {
        if (StringUtils.isEmpty(conditionFields))
        {
            throw new ServiceException(messagePrefix + "");
        }
        for (String field : conditionFields)
        {
            if (StringUtils.isBlank(field) || !columnNames.contains(field))
            {
                throw new ServiceException(messagePrefix + StringUtils.defaultString(field));
            }
        }
    }

    private boolean isSupportedRuleType(String type)
    {
        return "EXISTS".equals(type) || "NOT_EXISTS".equals(type) || "FIELD_EQUALS".equals(type)
                || "FIELD_NOT_EQUALS".equals(type) || "FIELD_IN".equals(type) || "NUMBER_GTE".equals(type);
    }

    private boolean requiresRuleTargetField(String type)
    {
        return "FIELD_EQUALS".equals(type) || "FIELD_NOT_EQUALS".equals(type) || "FIELD_IN".equals(type)
                || "NUMBER_GTE".equals(type);
    }

    private boolean requiresExpectedValue(String type)
    {
        return "FIELD_EQUALS".equals(type) || "FIELD_NOT_EQUALS".equals(type);
    }

    private boolean isSupportedEffectType(String type)
    {
        return "UPDATE_FIELD".equals(type) || "INSERT_ROW".equals(type) || "UPDATE_FIELDS".equals(type)
                || "SET_STATUS".equals(type) || "INCREASE_NUMBER".equals(type) || "DECREASE_NUMBER".equals(type);
    }

    private boolean requiresEffectTargetField(String type)
    {
        return "UPDATE_FIELD".equals(type) || "SET_STATUS".equals(type) || "INCREASE_NUMBER".equals(type)
                || "DECREASE_NUMBER".equals(type);
    }

    private boolean requiresEffectValues(String type)
    {
        return "INSERT_ROW".equals(type) || "UPDATE_FIELDS".equals(type);
    }

    private void validateSafeValue(String value, Set<String> columnNames)
    {
        if (StringUtils.isBlank(value))
        {
            return;
        }
        Matcher matcher = PARAM_PLACEHOLDER.matcher(value);
        if (matcher.matches())
        {
            String field = matcher.group(1);
            if (!columnNames.contains(field))
            {
                throw new ServiceException("Business effect value references missing field: " + field);
            }
            return;
        }
        if ("${now}".equals(value))
        {
            return;
        }
        if (value.contains(";") || value.contains("--") || value.contains("`") || value.length() > 200)
        {
            throw new ServiceException("Business effect value is invalid");
        }
    }

    private void validateNumberExpression(String value, Set<String> columnNames, String message)
    {
        if (StringUtils.isBlank(value))
        {
            throw new ServiceException(message);
        }
        Matcher matcher = PARAM_PLACEHOLDER.matcher(value);
        if (matcher.matches())
        {
            String field = matcher.group(1);
            if (!columnNames.contains(field))
            {
                throw new ServiceException("Business effect value references missing field: " + field);
            }
            return;
        }
        if (!NUMBER_LITERAL.matcher(value).matches())
        {
            throw new ServiceException(message);
        }
    }

    private Set<String> collectTableNames(List<DatabaseTableDesign> tables)
    {
        Set<String> tableNames = new HashSet<String>();
        if (tables == null)
        {
            return tableNames;
        }
        for (DatabaseTableDesign table : tables)
        {
            if (table != null && StringUtils.isNotBlank(table.getTableName()))
            {
                tableNames.add(table.getTableName().toLowerCase(Locale.ENGLISH));
            }
        }
        return tableNames;
    }

    private Set<String> collectColumnNames(List<DatabaseTableDesign> tables)
    {
        Set<String> columnNames = new HashSet<String>();
        if (tables == null)
        {
            return columnNames;
        }
        for (DatabaseTableDesign table : tables)
        {
            if (table == null || table.getColumns() == null)
            {
                continue;
            }
            for (DatabaseColumnDesign column : table.getColumns())
            {
                if (column != null && StringUtils.isNotBlank(column.getColumnName()))
                {
                    columnNames.add(column.getColumnName().toLowerCase(Locale.ENGLISH));
                }
            }
        }
        return columnNames;
    }
}
