import * as v from "valibot";

export function capitalize(input: string, firstOfAllWords?: boolean): string {
    // capitalize first letter of input
    if (!firstOfAllWords) {
        return input.charAt(0).toUpperCase() + input.slice(1);
    }
    let out = "";
    for (const word of input.split(" ")) {
        out += word.charAt(0).toUpperCase() + word.slice(1) + " ";
    }
    return out.slice(0, -1);
}

export function camelToSpacedPascal(input: string): string {
    let result = "";
    let previousChar = "";
    for (const char of input) {
        if (char === char.toUpperCase() && previousChar !== " ") {
            result += " ";
        }
        result += char;
        previousChar = char;
    }
    return result.charAt(0).toUpperCase() + result.slice(1);
}

export function snakeToCamel(input: string): string {
    if (!input) {
        return input;
    }
    // also account for numbers and kebab-case
    const splits = input.split(/[-_]/);
    let result = splits[0];
    for (const split of splits.slice(1)) {
        result += capitalize(split, true);
    }
    return result ?? "";
}

export function snakeToSpacedPascal(input: string): string {
    return camelToSpacedPascal(snakeToCamel(input));
}

export function spacedPascalToSnake(input: string): string {
    return input.split(" ").join("_").toLowerCase();
}

export function convertDashedLowerToTitleCase(input: string): string {
    return input
        .split("-")
        .map(
            (word) =>
                word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(),
        )
        .join(" "); // Join the words with a space
}

export function encodeCursor<T>(cursor: T): string {
    try {
        // Convert the object to a JSON string
        const jsonString = JSON.stringify(cursor);
        // Convert to UTF-8 bytes, then base64
        return btoa(
            encodeURIComponent(jsonString).replace(/%([0-9A-F]{2})/g, (_, p1) =>
                String.fromCharCode(parseInt(p1, 16)),
            ),
        );
    } catch (error) {
        console.error("Error encoding cursor:", error);
        throw new Error("Failed to encode cursor");
    }
}

export function decodeCursor<T>(
    cursor: string,
    parser: v.BaseSchema<any, T, any>,
) {
    try {
        // Decode base64 back to UTF-8 string
        const decoded = decodeURIComponent(
            Array.prototype.map
                .call(atob(cursor), (c) => {
                    return (
                        "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2)
                    );
                })
                .join(""),
        );
        // Parse back to object
        const parsedData = JSON.parse(decoded);
        const result = v.safeParse(parser, parsedData);
        return result.success
            ? { success: true, data: result.output as T }
            : {
                  success: false,
                  error: new Error(
                      result.issues.map((i) => i.message).join(", "),
                  ),
                  data: undefined,
              };
    } catch (error) {
        console.error("Error decoding cursor:", error);
        return { error: new Error("Failed to decode cursor"), data: undefined };
    }
}
