package com.ruoyi.generator.service.front;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
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.DatabaseColumnDesign;
import com.ruoyi.generator.domain.front.dto.DatabaseDesignResponse;
import com.ruoyi.generator.domain.front.dto.DatabaseTableDesign;

@Component
public class AiDatabaseSchemaValidator
{
    private static final Pattern NAME = Pattern.compile("^[a-z][a-z0-9_]{1,63}$");
    private static final Pattern BIGINT = Pattern.compile("^bigint(\\(20\\))?$");
    private static final Pattern INT = Pattern.compile("^int(\\(11\\))?$");
    private static final Pattern VARCHAR = Pattern.compile("^varchar\\(([1-9][0-9]{0,2}|1000)\\)$");
    private static final Pattern CHAR = Pattern.compile("^char\\(([1-9][0-9]?)\\)$");
    private static final Pattern DECIMAL = Pattern.compile("^decimal\\(([1-9][0-9]?),([0-9][0-9]?)\\)$");
    private static final Set<String> EXACT_TYPES = new HashSet<String>(Arrays.asList("datetime", "date", "text", "longtext"));
    private static final List<String> DANGEROUS_SQL = Arrays.asList(
            "drop", "truncate", "alter user", "grant", "revoke", "load_file", "into outfile");

    public boolean isValidName(String name)
    {
        return StringUtils.isNotBlank(name) && NAME.matcher(name).matches();
    }

    public boolean isAllowedColumnType(String columnType)
    {
        if (StringUtils.isBlank(columnType))
        {
            return false;
        }
        String type = columnType.trim().toLowerCase(Locale.ENGLISH);
        return EXACT_TYPES.contains(type) || BIGINT.matcher(type).matches() || INT.matcher(type).matches()
                || VARCHAR.matcher(type).matches() || CHAR.matcher(type).matches() || isAllowedDecimal(type);
    }

    private boolean isAllowedDecimal(String type)
    {
        Matcher matcher = DECIMAL.matcher(type);
        if (!matcher.matches())
        {
            return false;
        }
        int precision = Integer.parseInt(matcher.group(1));
        int scale = Integer.parseInt(matcher.group(2));
        return precision >= 1 && precision <= 65 && scale >= 0 && scale <= 30 && scale <= precision;
    }

    public boolean isSafeSql(String sql)
    {
        if (sql == null)
        {
            return true;
        }
        String lowerSql = sql.toLowerCase(Locale.ENGLISH);
        for (String dangerous : DANGEROUS_SQL)
        {
            if (lowerSql.contains(dangerous))
            {
                return false;
            }
        }
        return true;
    }

    public void validate(DatabaseDesignResponse response, int maxTables, int maxColumnsPerTable)
    {
        if (response == null || response.getTables() == null || response.getTables().isEmpty())
        {
            throw new ServiceException("至少需要一张数据表");
        }
        if (response.getTables().size() > maxTables)
        {
            throw new ServiceException("数据表数量超过限制");
        }
        for (DatabaseTableDesign table : response.getTables())
        {
            validateTable(table, maxColumnsPerTable);
        }
    }

    private void validateTable(DatabaseTableDesign table, int maxColumnsPerTable)
    {
        if (table == null || !isValidName(table.getTableName()))
        {
            throw new ServiceException("表名只能包含小写字母、数字和下划线");
        }
        if (table.getColumns() == null || table.getColumns().isEmpty())
        {
            throw new ServiceException("每张表至少需要一个字段");
        }
        if (table.getColumns().size() > maxColumnsPerTable)
        {
            throw new ServiceException("字段数量超过限制");
        }
        int primaryKeyCount = 0;
        for (DatabaseColumnDesign column : table.getColumns())
        {
            if (column == null || !isValidName(column.getColumnName()))
            {
                throw new ServiceException("字段名只能包含小写字母、数字和下划线");
            }
            if (!isAllowedColumnType(column.getColumnType()))
            {
                throw new ServiceException("字段类型不在允许范围内");
            }
            if ("1".equals(column.getIsPk()))
            {
                primaryKeyCount++;
            }
        }
        if (primaryKeyCount != 1)
        {
            throw new ServiceException("每张表必须且只能有一个主键");
        }
    }
}
