import React, {ChangeEventHandler, useState} from 'react';

export function ValidatingInputBigIntField({
                                               id,
                                               label,
                                               value = 0n,
                                               constraints = undefined,
                                               validatorFn = (value) => {
                                                   return {valid: true, value: BigInt(value)};
                                               },
                                               disabled = false,
                                               onChange = () => {
                                               }
                                           }:
                                           {
                                               id: string;
                                               label: string;
                                               value?: bigint;
                                               constraints?: { min?: bigint; max?: bigint; };
                                               validatorFn: ValidatorFunction<string, bigint>;
                                               disabled: boolean;
                                               onChange: (v: bigint) => void;
                                           }) {
    const [error, setError] = useState<string>();

    const handleValueChange: ChangeEventHandler<HTMLInputElement> = (e) => {
        const value = e.target.value;
        const validation = validatorFn(value);
        if (!validation.valid) {
            setError(validation.message);
        } else {
            setError(undefined);
        }
        if (validation.value !== undefined) {
            onChange(validation.value);
        }
    };
    return (
        <>
            <label htmlFor={id}>{label}: </label>
            <input id={id}
                   type={"number"}
                   onChange={handleValueChange}
                   value={value?.toString() || ""}
                   min={constraints?.min?.toString()}
                   max={constraints?.max?.toString()}
                   disabled={disabled}
            />
            <span>{error}</span>
        </>
    );
}

export function ValidatingInputNumberField({
                                               id,
                                               label,
                                               value = 0,
                                               constraints = undefined,
                                               validatorFn = (value) => {
                                                   return {valid: true, value: Number(value)};
                                               },
                                               disabled = false,
                                               onChange = () => {
                                               }
                                           }:
                                           {
                                               id: string;
                                               label: string;
                                               value?: number;
                                               constraints?: { min?: number; max?: number; };
                                               validatorFn: ValidatorFunction<string, number>;
                                               disabled: boolean;
                                               onChange: (v: number) => void;
                                           }) {
    const [error, setError] = useState<string>();

    const handleValueChange: ChangeEventHandler<HTMLInputElement> = (e) => {
        const value = e.target.value;
        const validation = validatorFn(value);
        if (!validation.valid) {
            setError(validation.message);
        } else {
            setError(undefined);
        }
        if (validation.value) {
            onChange(validation.value);
        }
    };
    return (
        <>
            <label htmlFor={id}>{label}: </label>
            <input id={id}
                   type={"number"}
                   onChange={handleValueChange}
                   value={value || ""}
                   min={constraints?.min}
                   max={constraints?.max}
                   disabled={disabled}
            />
            <span>{error}</span>
        </>
    );
}

export function ValidatingInputRegExpField({
                                               id,
                                               label,
                                               value = undefined,
                                               constraints = undefined,
                                               validatorFn = (value) => {
                                                   if (constraints === undefined) {
                                                       console.log("no constraints, returning VALID")
                                                       return {valid: true, value};
                                                   }
                                                   const allValid = constraints
                                                           .map(expr => expr.test(value))
                                                           .reduce((prev, curr) => prev && curr)
                                                       ?? true;
                                                   console.log("valid?", allValid);
                                                   return {valid: allValid, value: allValid ? value : undefined};
                                               },
                                               disabled = false,
                                               onChange = () => {
                                               },
                                               placeholder = ""
                                           }:
                                           {
                                               id: string;
                                               label: string;
                                               value?: string;
                                               constraints?: RegExp[];
                                               validatorFn?: ValidatorFunction<string, string>;
                                               disabled: boolean;
                                               onChange: (v: string) => void;
                                               placeholder?: string;
                                           }) {
    const [error, setError] = useState<string>();

    const handleValueChange: ChangeEventHandler<HTMLInputElement> = (e) => {
        const value = e.target.value;
        const validation = validatorFn(value);
        if (!validation.valid) {
            setError(validation.message);
        } else {
            setError(undefined);
        }
        if (validation.value !== undefined) {
            console.log("setting value to:", validation.value);
            onChange(validation.value);
        }
    };
    return (
        <>
            <label htmlFor={id}>{label}: </label>
            <input id={id}
                   type={"text"}
                   placeholder={placeholder}
                   onChange={handleValueChange}
                   value={value?.toString() || ""}
                   disabled={disabled}
            />
            <span>{error}</span>
        </>
    );
}

export interface Validation<T> {
    valid: boolean;
    message?: string;
    value?: T;
}

export type ValidatorFunction<I, T> = (v: I) => Validation<T>;
