import { Accessor, createMemo } from "solid-js";

export interface Validation<T> {
    id: string; // snake case identifier that is "touched"
    name: string;
    required?: boolean;
    touched: Accessor<Record<string, boolean>>;
    field: Accessor<T>;
    fieldBlur?: Accessor<T>;
    isValidFunc?: (input: string, ...args: any) => boolean;
    invalidMsg?: string;
}

export function createValidation(validation: Validation<string>): Accessor<string> {
    const { id, name, required, touched, isValidFunc, invalidMsg } = validation;
    const field = () => validation.field().trim();
    // When no blur accessor is provided, fall back to the live value so the
    // "has value but not blurred yet" guard is always false and validation
    // runs against the live value instead.
    const fieldBlur = validation.fieldBlur ? () => validation.fieldBlur!().trim() : field;

    return createMemo(() => {
        if (!touched()[id] || (field() && !fieldBlur()) || (isValidFunc && isValidFunc(field()))) return "";
        if (required && !field()) return `${name} is required`;
        if (isValidFunc && !isValidFunc(fieldBlur())) return invalidMsg ?? `${name} is invalid`;
        return "";
    });
}

export function isPhoneNumberValid(phoneNumber: string): boolean {
    phoneNumber = phoneNumber.replace(/\D/g, "");
    return phoneNumber.length == 10
}

export function isEmailValid(email: string): boolean {
    const regex = /^[-!#$%&'*+\/0-9=?A-Z^_a-z`{|}~](\.?[-!#$%&'*+\/0-9=?A-Z^_a-z`{|}~])*@[a-zA-Z0-9](-*\.?[a-zA-Z0-9])*\.[a-zA-Z](-?[a-zA-Z0-9])+$/;

    if (!email) return false;

    let emailParts = email.split('@');

    if (emailParts.length !== 2) return false;

    let account = emailParts[0];
    let address = emailParts[1];

    if (account.length > 64) return false;

    else if (address.length > 255) return false;

    let domainParts = address.split('.');

    if (domainParts.some(function (part) {
        return part.length > 63;
    })) return false;

    return regex.test(email);
}

export function isUrlValid(url: string): boolean {
    const regex = /[(http(s)?):\/\/(www\.)?a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/;

    return regex.test(url);
}

export function isZipCodeValid(zip: string): boolean {
    const rawZip = String(zip).replace(/\D/g, "");
    return rawZip.length == 5 || rawZip.length == 9
}

export function isTaxIdValid(id: string): boolean {
    const rawId = String(id).replace(/\D/g, "");
    return rawId.length == 9;
}

// isAtLeastMinChars checks if the input string is at least "min" characters long
// and returns a boolean, true if valid, false if not.
export function isAtLeastMinChars(input: string, min:number):boolean {
    return input.length >= min;
}

// isWithinMaxChars checks if the input string is at most "max" characters long
// and returns a boolean, true if valid, false if not.
export function isWithinMaxChars(input: string, max:number):boolean {
    return input.length <= max;
}

// isNameValid checks if the name string consists of only letters, spaces, hyphens, and apostrophes
// and returns a boolean, true if valid, false if not.
export function isNameValid(name: string): boolean {
    const regex = /^[\p{L}]*[\p{L} '\-]*[\p{L}]$/u;
    
    return regex.test(name);
}

// isUsernameValid checks if the username contains 5-50 characters and only consists of alphanumeric
// characters. Returns an error message if invalid, empty string if valid.
export function isUsernameValid(username: string): string {
    if (username.length < 5 || username.length > 50) return "Username must have 5-50 characters"
    
    const regex = /^[A-Za-z0-9]*$/;
    if (!regex.test(username)) return "Username must only contain alphanumeric characters"

    return ""; // Valid
}