"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Value = exports.fileInfoParser = void 0;
const util_1 = require("../../../util");
const zod_1 = require("zod");
/** Build a union-of-literals validator from object keys, falling back to z.string() when empty */
function literalKeysValidator(values) {
    const keys = Object.keys(values);
    if (keys.length === 0)
        return zod_1.z.string();
    return zod_1.z.union(keys.map((x) => zod_1.z.literal(x)));
}
/** Zod schema for a file upload result — validates `{ path, commitment: { hash, size } }`. */
exports.fileInfoParser = zod_1.z.object({
    path: zod_1.z.string(),
    commitment: zod_1.z.object({ hash: zod_1.z.string(), size: zod_1.z.number() }),
});
const testForAsRequiredParser = (0, util_1.once)(() => (v) => zod_1.z.object({ required: zod_1.z.literal(true) }).safeParse(v).success);
function asRequiredParser(parser, input) {
    if (testForAsRequiredParser()(input))
        return parser;
    return parser.nullable();
}
/**
 * Core builder class for defining a single form field in a service configuration spec.
 *
 * Each static factory method (e.g. `Value.text()`, `Value.toggle()`, `Value.select()`) creates
 * a typed `Value` instance representing a specific field type. Dynamic variants (e.g. `Value.dynamicText()`)
 * allow the field options to be computed lazily at runtime.
 *
 * Use with {@link InputSpec} to compose complete form specifications.
 *
 * @typeParam Type - The runtime type this field produces when filled in
 * @typeParam StaticValidatedAs - The compile-time validated type (usually same as Type)
 * @typeParam OuterType - The parent form's type context (used by dynamic variants)
 */
class Value {
    constructor(build, validator) {
        this.build = build;
        this.validator = validator;
        this._TYPE = null;
        this._PARTIAL = null;
    }
    /**
     * @description Displays a boolean toggle to enable/disable
     * @example
     * ```
      toggleExample: Value.toggle({
        // required
        name: 'Toggle Example',
        default: true,
      
        // optional
        description: null,
        warning: null,
        immutable: false,
      }),
      * ```
     */
    static toggle(a) {
        const validator = zod_1.z.boolean();
        return new Value(async () => ({
            spec: {
                description: null,
                warning: null,
                type: 'toggle',
                disabled: false,
                immutable: a.immutable ?? false,
                ...a,
            },
            validator,
        }), validator);
    }
    /** Like {@link Value.toggle} but options are resolved lazily at runtime via a builder function. */
    static dynamicToggle(a) {
        const validator = zod_1.z.boolean();
        return new Value(async (options) => ({
            spec: {
                description: null,
                warning: null,
                type: 'toggle',
                disabled: false,
                immutable: false,
                ...(await a(options)),
            },
            validator,
        }), validator);
    }
    /**
     * @description Displays a text input field
     * @example
     * ```
      textExample: Value.text({
        // required
        name: 'Text Example',
        required: false,
        default: null,
      
        // optional
        description: null,
        placeholder: null,
        warning: null,
        generate: null,
        inputmode: 'text',
        masked: false,
        minLength: null,
        maxLength: null,
        patterns: [],
        immutable: false,
      }),
      * ```
      */
    static text(a) {
        const validator = asRequiredParser(zod_1.z.string(), a);
        return new Value(async () => ({
            spec: {
                type: 'text',
                description: null,
                warning: null,
                masked: false,
                placeholder: null,
                minLength: null,
                maxLength: null,
                patterns: [],
                inputmode: 'text',
                disabled: false,
                immutable: a.immutable ?? false,
                generate: a.generate ?? null,
                ...a,
            },
            validator,
        }), validator);
    }
    /** Like {@link Value.text} but options are resolved lazily at runtime via a builder function. */
    static dynamicText(getA) {
        return new Value(async (options) => {
            const a = await getA(options);
            return {
                spec: {
                    type: 'text',
                    description: null,
                    warning: null,
                    masked: false,
                    placeholder: null,
                    minLength: null,
                    maxLength: null,
                    patterns: [],
                    inputmode: 'text',
                    disabled: false,
                    immutable: false,
                    generate: a.generate ?? null,
                    ...a,
                },
                validator: asRequiredParser(zod_1.z.string(), a),
            };
        }, zod_1.z.string().nullable());
    }
    /**
     * @description Displays a large textarea field for long form entry.
     * @example
     * ```
      textareaExample: Value.textarea({
        // required
        name: 'Textarea Example',
        required: false,
        default: null,
      
        // optional
        description: null,
        placeholder: null,
        warning: null,
        minLength: null,
        maxLength: null,
        minRows: 3
        maxRows: 6
        immutable: false,
      }),
      * ```
      */
    static textarea(a) {
        const validator = asRequiredParser(zod_1.z.string(), a);
        return new Value(async () => {
            const built = {
                description: null,
                warning: null,
                minLength: null,
                maxLength: null,
                patterns: [],
                minRows: 3,
                maxRows: 6,
                placeholder: null,
                type: 'textarea',
                disabled: false,
                immutable: a.immutable ?? false,
                ...a,
            };
            return { spec: built, validator };
        }, validator);
    }
    /** Like {@link Value.textarea} but options are resolved lazily at runtime via a builder function. */
    static dynamicTextarea(getA) {
        return new Value(async (options) => {
            const a = await getA(options);
            return {
                spec: {
                    description: null,
                    warning: null,
                    minLength: null,
                    maxLength: null,
                    patterns: [],
                    minRows: 3,
                    maxRows: 6,
                    placeholder: null,
                    type: 'textarea',
                    disabled: false,
                    immutable: false,
                    ...a,
                },
                validator: asRequiredParser(zod_1.z.string(), a),
            };
        }, zod_1.z.string().nullable());
    }
    /**
     * @description Displays a number input field
     * @example
     * ```
      numberExample: Value.number({
        // required
        name: 'Number Example',
        required: false,
        default: null,
        integer: true,
      
        // optional
        description: null,
        placeholder: null,
        warning: null,
        min: null,
        max: null,
        immutable: false,
        step: null,
        units: null,
      }),
      * ```
      */
    static number(a) {
        const validator = asRequiredParser(zod_1.z.number(), a);
        return new Value(() => ({
            spec: {
                type: 'number',
                description: null,
                warning: null,
                min: null,
                max: null,
                step: null,
                units: null,
                placeholder: null,
                disabled: false,
                immutable: a.immutable ?? false,
                ...a,
            },
            validator,
        }), validator);
    }
    /** Like {@link Value.number} but options are resolved lazily at runtime via a builder function. */
    static dynamicNumber(getA) {
        return new Value(async (options) => {
            const a = await getA(options);
            return {
                spec: {
                    type: 'number',
                    description: null,
                    warning: null,
                    min: null,
                    max: null,
                    step: null,
                    units: null,
                    placeholder: null,
                    disabled: false,
                    immutable: false,
                    ...a,
                },
                validator: asRequiredParser(zod_1.z.number(), a),
            };
        }, zod_1.z.number().nullable());
    }
    /**
     * @description Displays a browser-native color selector.
     * @example
     * ```
      colorExample: Value.color({
        // required
        name: 'Color Example',
        required: false,
        default: null,
      
        // optional
        description: null,
        warning: null,
        immutable: false,
      }),
      * ```
      */
    static color(a) {
        const validator = asRequiredParser(zod_1.z.string(), a);
        return new Value(() => ({
            spec: {
                type: 'color',
                description: null,
                warning: null,
                disabled: false,
                immutable: a.immutable ?? false,
                ...a,
            },
            validator,
        }), validator);
    }
    /** Like {@link Value.color} but options are resolved lazily at runtime via a builder function. */
    static dynamicColor(getA) {
        return new Value(async (options) => {
            const a = await getA(options);
            return {
                spec: {
                    type: 'color',
                    description: null,
                    warning: null,
                    disabled: false,
                    immutable: false,
                    ...a,
                },
                validator: asRequiredParser(zod_1.z.string(), a),
            };
        }, zod_1.z.string().nullable());
    }
    /**
     * @description Displays a browser-native date/time selector.
     * @example
     * ```
      datetimeExample: Value.datetime({
        // required
        name: 'Datetime Example',
        required: false,
        default: null,
      
        // optional
        description: null,
        warning: null,
        immutable: false,
        inputmode: 'datetime-local',
        min: null,
        max: null,
      }),
      * ```
      */
    static datetime(a) {
        const validator = asRequiredParser(zod_1.z.string(), a);
        return new Value(() => ({
            spec: {
                type: 'datetime',
                description: null,
                warning: null,
                inputmode: 'datetime-local',
                min: null,
                max: null,
                step: null,
                disabled: false,
                immutable: a.immutable ?? false,
                ...a,
            },
            validator,
        }), validator);
    }
    /** Like {@link Value.datetime} but options are resolved lazily at runtime via a builder function. */
    static dynamicDatetime(getA) {
        return new Value(async (options) => {
            const a = await getA(options);
            return {
                spec: {
                    type: 'datetime',
                    description: null,
                    warning: null,
                    inputmode: 'datetime-local',
                    min: null,
                    max: null,
                    disabled: false,
                    immutable: false,
                    ...a,
                },
                validator: asRequiredParser(zod_1.z.string(), a),
            };
        }, zod_1.z.string().nullable());
    }
    /**
     * @description Displays a select modal with radio buttons, allowing for a single selection.
     * @example
     * ```
      selectExample: Value.select({
        // required
        name: 'Select Example',
        default: 'radio1',
        values: {
          radio1: 'Radio 1',
          radio2: 'Radio 2',
        },
      
        // optional
        description: null,
        warning: null,
        immutable: false,
        disabled: false,
      }),
      * ```
      */
    static select(a) {
        const validator = literalKeysValidator(a.values);
        return new Value(() => ({
            spec: {
                description: null,
                warning: null,
                type: 'select',
                disabled: false,
                immutable: a.immutable ?? false,
                ...a,
            },
            validator,
        }), validator);
    }
    /** Like {@link Value.select} but options are resolved lazily at runtime via a builder function. */
    static dynamicSelect(getA) {
        return new Value(async (options) => {
            const a = await getA(options);
            return {
                spec: {
                    description: null,
                    warning: null,
                    type: 'select',
                    disabled: false,
                    immutable: false,
                    ...a,
                },
                validator: literalKeysValidator(a.values),
            };
        }, zod_1.z.string());
    }
    /**
     * @description Displays a select modal with checkboxes, allowing for multiple selections.
     * @example
     * ```
      multiselectExample: Value.multiselect({
        // required
        name: 'Multiselect Example',
        values: {
          option1: 'Option 1',
          option2: 'Option 2',
        },
        default: [],
      
        // optional
        description: null,
        warning: null,
        immutable: false,
        disabled: false,
        minlength: null,
        maxLength: null,
      }),
      * ```
      */
    static multiselect(a) {
        const validator = zod_1.z.array(literalKeysValidator(a.values));
        return new Value(() => ({
            spec: {
                type: 'multiselect',
                minLength: null,
                maxLength: null,
                warning: null,
                description: null,
                disabled: false,
                immutable: a.immutable ?? false,
                ...a,
            },
            validator,
        }), validator);
    }
    /** Like {@link Value.multiselect} but options are resolved lazily at runtime via a builder function. */
    static dynamicMultiselect(getA) {
        return new Value(async (options) => {
            const a = await getA(options);
            return {
                spec: {
                    type: 'multiselect',
                    minLength: null,
                    maxLength: null,
                    warning: null,
                    description: null,
                    disabled: false,
                    immutable: false,
                    ...a,
                },
                validator: zod_1.z.array(literalKeysValidator(a.values)),
            };
        }, zod_1.z.array(zod_1.z.string()));
    }
    /**
     * @description Display a collapsable grouping of additional fields, a "sub form". The second value is the inputSpec spec for the sub form.
     * @example
     * ```
      objectExample: Value.object(
        {
          // required
          name: 'Object Example',
      
          // optional
          description: null,
          warning: null,
        },
        InputSpec.of({}),
      ),
      * ```
      */
    static object(a, spec) {
        const value = new Value(async (options) => {
            const built = await spec.build(options);
            return {
                spec: {
                    type: 'object',
                    description: null,
                    warning: null,
                    ...a,
                    spec: built.spec,
                },
                validator: built.validator,
            };
        }, spec.validator);
        value._objectSpec = { inputSpec: spec, params: a };
        return value;
    }
    /**
     * Displays a file upload input field.
     *
     * @param a.extensions - Allowed file extensions (e.g. `[".pem", ".crt"]`)
     * @param a.required - Whether a file must be selected
     */
    static file(a) {
        const buildValue = {
            type: 'file',
            description: null,
            warning: null,
            ...a,
        };
        return new Value(() => ({
            spec: {
                ...buildValue,
            },
            validator: asRequiredParser(exports.fileInfoParser, a),
        }), asRequiredParser(exports.fileInfoParser, a));
    }
    /** Like {@link Value.file} but options are resolved lazily at runtime via a builder function. */
    static dynamicFile(a) {
        return new Value(async (options) => {
            const spec = {
                type: 'file',
                description: null,
                warning: null,
                ...(await a(options)),
            };
            return {
                spec,
                validator: asRequiredParser(exports.fileInfoParser, spec),
            };
        }, exports.fileInfoParser.nullable());
    }
    /**
     * @description Displays a dropdown, allowing for a single selection. Depending on the selection, a different object ("sub form") is presented.
     * @example
     * ```
      unionExample: Value.union(
        {
          // required
          name: 'Union Example',
          default: 'option1',
      
          // optional
          description: null,
          warning: null,
          disabled: false,
          immutable: false,
        },
        Variants.of({
          option1: {
            name: 'Option 1',
            spec: InputSpec.of({}),
          },
          option2: {
            name: 'Option 2',
            spec: InputSpec.of({}),
          },
        }),
      ),
      * ```
      */
    static union(a) {
        return new Value(async (options) => {
            const built = await a.variants.build(options);
            return {
                spec: {
                    type: 'union',
                    description: null,
                    warning: null,
                    disabled: false,
                    ...a,
                    variants: built.spec,
                    immutable: a.immutable ?? false,
                },
                validator: built.validator,
            };
        }, a.variants.validator);
    }
    static dynamicUnion(getA, validator = zod_1.z.any()) {
        return new Value(async (options) => {
            const newValues = await getA(options);
            const built = await newValues.variants.build(options);
            return {
                spec: {
                    type: 'union',
                    description: null,
                    warning: null,
                    ...newValues,
                    variants: built.spec,
                    immutable: false,
                },
                validator: built.validator,
            };
        }, validator);
    }
    /**
     * @description Presents an interface to add/remove/edit items in a list.
     * @example
     * In this example, we create a list of text inputs.
     *
     * ```
      listExampleText: Value.list(
        List.text(
          {
            // required
            name: 'Text List',
  
            // optional
            description: null,
            warning: null,
            default: [],
            minLength: null,
            maxLength: null,
          },
          {
            // required
            patterns: [],
  
            // optional
            placeholder: null,
            generate: null,
            inputmode: 'url',
            masked: false,
            minLength: null,
            maxLength: null,
          },
        ),
      ),
     * ```
     * @example
     * In this example, we create a list of objects.
     *
     * ```
      listExampleObject: Value.list(
        List.obj(
          {
            // required
            name: 'Object List',
  
            // optional
            description: null,
            warning: null,
            default: [],
            minLength: null,
            maxLength: null,
          },
          {
            // required
            spec: InputSpec.of({}),
  
            // optional
            displayAs: null,
            uniqueBy: null,
          },
        ),
      ),
     * ```
     */
    static list(a) {
        return new Value((options) => a.build(options), a.validator);
    }
    static hidden(parser = zod_1.z.any()) {
        return new Value(async () => {
            return {
                spec: {
                    type: 'hidden',
                },
                validator: parser,
            };
        }, parser);
    }
    /**
     * @description Provides a way to define a hidden field with a static value. Useful for tracking
     * @example
     * ```
      hiddenExample: Value.hidden(),
     * ```
     */
    static dynamicHidden(getParser) {
        return new Value(async (options) => {
            const validator = await getParser(options);
            return {
                spec: {
                    type: 'hidden',
                },
                validator,
            };
        }, zod_1.z.any());
    }
    /**
     * Returns a new Value that produces the same field spec but with `disabled` set to the given message.
     * The field remains in the form but cannot be edited by the user.
     *
     * @param message - The reason the field is disabled, displayed to the user
     */
    withDisabled(message) {
        const original = this;
        const v = new Value(async (options) => {
            const built = await original.build(options);
            return {
                spec: { ...built.spec, disabled: message },
                validator: built.validator,
            };
        }, this.validator);
        v._objectSpec = this._objectSpec;
        return v;
    }
    /**
     * Transforms the validated output value using a mapping function.
     * The form field itself remains unchanged, but the value is transformed after validation.
     *
     * @param fn - A function to transform the validated value
     */
    map(fn) {
        return new Value(async (options) => {
            const built = await this.build(options);
            return {
                spec: built.spec,
                validator: built.validator.transform(fn),
            };
        }, this.validator.transform(fn));
    }
}
exports.Value = Value;
//# sourceMappingURL=value.js.map