import unicode from "unicode-properties";
import { minMaxLengthForType, minMaxValueForType, } from "../../attributes/Constraints.js";
import { funPrefixNamer } from "../../Naming.js";
import { combineWords, firstUpperWordStyle, splitIntoWords, utf16LegalizeCharacters, } from "../../support/Strings.js";
import { panic } from "../../support/Support.js";
import { ArrayType, EnumType, UnionType, } from "../../Type/index.js";
import { nullableFromUnion } from "../../Type/TypeUtils.js";
import { keywords } from "./constants.js";
export var AccessModifier;
(function (AccessModifier) {
    AccessModifier["None"] = "None";
    AccessModifier["Public"] = "Public";
    AccessModifier["Internal"] = "Internal";
})(AccessModifier || (AccessModifier = {}));
export function noFollow(t) {
    return t;
}
export function needTransformerForType(t) {
    if (t instanceof UnionType) {
        const maybeNullable = nullableFromUnion(t);
        if (maybeNullable === null)
            return "automatic";
        if (needTransformerForType(maybeNullable) === "manual")
            return "nullable";
        return "none";
    }
    if (t instanceof ArrayType) {
        const itemsNeed = needTransformerForType(t.items);
        if (itemsNeed === "manual" || itemsNeed === "nullable")
            return "automatic";
        return "none";
    }
    if (t instanceof EnumType)
        return "automatic";
    if (t.kind === "double")
        return minMaxValueForType(t) !== undefined ? "manual" : "none";
    if (t.kind === "integer-string" || t.kind === "bool-string")
        return "manual";
    if (t.kind === "string") {
        return minMaxLengthForType(t) !== undefined ? "manual" : "none";
    }
    return "none";
}
export function alwaysApplyTransformation(xf) {
    const t = xf.targetType;
    if (t instanceof EnumType)
        return true;
    if (t instanceof UnionType)
        return nullableFromUnion(t) === null;
    return false;
}
/**
 * The C# type for a given transformed string type.
 */
export function csTypeForTransformedStringType(t) {
    switch (t.kind) {
        case "date-time":
            return "DateTimeOffset";
        case "uuid":
            return "Guid";
        case "uri":
            return "Uri";
        default:
            return panic(`Transformed string type ${t.kind} not supported`);
    }
}
export const namingFunction = funPrefixNamer("namer", csNameStyle);
export const namingFunctionKeep = funPrefixNamer("namerKeep", csNameStyleKeep);
// FIXME: Make a Named?
export const denseJsonPropertyName = "J";
export const denseRequiredEnumName = "R";
export const denseNullValueHandlingEnumName = "N";
export function isStartCharacter(utf16Unit) {
    if (unicode.isAlphabetic(utf16Unit)) {
        return true;
    }
    return utf16Unit === 0x5f; // underscore
}
function isPartCharacter(utf16Unit) {
    const category = unicode.getCategory(utf16Unit);
    if (["Nd", "Pc", "Mn", "Mc"].includes(category)) {
        return true;
    }
    return isStartCharacter(utf16Unit);
}
const legalizeName = utf16LegalizeCharacters(isPartCharacter);
export function csNameStyle(original) {
    const words = splitIntoWords(original);
    return combineWords(words, legalizeName, firstUpperWordStyle, firstUpperWordStyle, firstUpperWordStyle, firstUpperWordStyle, "", isStartCharacter);
}
function csNameStyleKeep(original) {
    const words = [
        {
            word: original,
            isAcronym: false,
        },
    ];
    const result = combineWords(words, legalizeName, (x) => x, (x) => x, (x) => x, (x) => x, "", isStartCharacter);
    // @ts-expect-error needs strong type
    return keywords.includes(result) ? `@${result}` : result;
}
export function isValueType(t) {
    if (t instanceof UnionType) {
        return nullableFromUnion(t) === null;
    }
    return ["integer", "double", "bool", "enum", "date-time", "uuid"].includes(t.kind);
}
