package org.jcnc.snow.compiler.lexer.token;

import java.util.Set;

/**
 * {@code TokenFactory} 是一个静态工厂类，
 * 用于根据原始的词法单元（字符串文本）
 * 自动判断其词法类型，并构造对应的 {@link Token} 实例。
 * <p>
 * 支持自动识别语言关键字、内置类型名、合法标识符等。
 * 提供统一的 Token 构建机制，简化词法扫描器的处理逻辑。
 * </p>
 */
public class TokenFactory {

    /** 语言保留关键字集合 */
    private static final Set<String> KEYWORDS = Set.of(
            "module", "function", "parameter", "return_type",
            "body", "end", "if", "then", "else", "loop",
            "declare", "return", "import",
            "initializer", "condition", "update"
    );

    /** 内置类型标识集合 */
    private static final Set<String> TYPES = Set.of(
            "int", "string", "float", "bool", "void", "double"
    );

    /**
     * 创建一个带类型推断的 {@link Token} 实例。
     * <p>
     * 内部依据 {@code raw} 值匹配类型集合、关键字集合，
     * 若都不匹配则视为合法标识符，否则标记为 {@code UNKNOWN}。
     * </p>
     *
     * @param raw  原始词法单元文本
     * @param line Token 所在行号
     * @param col  Token 所在列号
     * @return 类型明确的 {@link Token} 实例
     */
    public static Token create(String raw, int line, int col) {
        TokenType type = determineTokenType(raw);
        return new Token(type, raw, line, col);
    }

    /**
     * 判定给定字符串所对应的 {@link TokenType}。
     * <p>
     * 优先级：类型（TYPE） > 关键字（KEYWORD） > 标识符（IDENTIFIER） > 未知（UNKNOWN）
     * </p>
     *
     * @param raw 原始词素字符串
     * @return 推断出的 Token 类型
     */
    private static TokenType determineTokenType(String raw) {
        if (isType(raw)) return TokenType.TYPE;
        if (isKeyword(raw)) return TokenType.KEYWORD;
        if (isIdentifier(raw)) return TokenType.IDENTIFIER;
        return TokenType.UNKNOWN;
    }

    /**
     * 判断是否为内置类型标识。
     *
     * @param raw 输入字符串
     * @return 若为类型名返回 {@code true}
     */
    private static boolean isType(String raw) {
        return TYPES.contains(raw);
    }

    /**
     * 判断是否为语言关键字。
     *
     * @param raw 输入字符串
     * @return 若为关键字返回 {@code true}
     */
    private static boolean isKeyword(String raw) {
        return KEYWORDS.contains(raw);
    }

    /**
     * 判断是否符合标识符命名规范。
     * <p>
     * 合法标识符需以字母或下划线开头，其后可包含字母、数字或下划线。
     * </p>
     *
     * @param raw 输入字符串
     * @return 若为合法标识符则返回 {@code true}
     */
    private static boolean isIdentifier(String raw) {
        return raw.matches("[a-zA-Z_][a-zA-Z0-9_]*");
    }
}