/*
 * Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.
 */

import * as z from "zod/v3";
import { defaultToZeroValue } from "./defaultToZeroValue.js";
import { unrecognized } from "./unrecognized.js";

export function string(): z.ZodType<string, z.ZodTypeDef, unknown> {
  return z.union([
    z.string(),

    // Null or undefined -> ""
    zodDefaultToZeroValue(""),

    // Any other value -> String(x)
    z.any().transform((x) => unrecognized(JSON.stringify(x))),
  ]);
}

export function boolean(): z.ZodType<boolean, z.ZodTypeDef, unknown> {
  return z.union([
    z.boolean(),

    // String "true" (case insensitive) -> true, "false" -> false
    z.string().transform((x, ctx) => {
      const lower = x.toLowerCase();
      if (lower === "true") return unrecognized(true);
      if (lower === "false") return unrecognized(false);
      ctx.addIssue({
        code: "invalid_type",
        expected: "boolean",
        received: "string",
      });
      return z.NEVER;
    }),

    zodDefaultToZeroValue(false),
  ]);
}

export function number(): z.ZodType<number, z.ZodTypeDef, unknown> {
  return z.union([
    z.number(),

    // String -> Number
    z.string().transform((x, ctx) => {
      const num = Number(x);
      if (isNaN(num)) {
        ctx.addIssue({
          code: "invalid_type",
          expected: "number",
          received: "string",
        });
        return z.NEVER;
      }
      return unrecognized(num);
    }),

    // Null or undefined -> 0
    zodDefaultToZeroValue(0),
  ]);
}

export function date(): z.ZodType<Date, z.ZodTypeDef, unknown> {
  return z.union([
    z.union([z.string(), zodDefaultToZeroValue(0)]).transform((x) =>
      new Date(x)
    ).pipe(z.date()),
    z.number().transform((x, ctx) => {
      const date = new Date(x);
      if (isNaN(date.getTime())) {
        ctx.addIssue({
          code: "invalid_type",
          expected: "date",
          received: "number",
        });
        return z.NEVER;
      }
      return unrecognized(date);
    }),
  ]);
}

export function literal<T extends string | number | boolean>(
  value: T,
): z.ZodType<T, z.ZodTypeDef, unknown> {
  return z.union([z.literal(value), zodDefaultToZeroValue(value)]);
}

export function optional<T extends z.ZodType>(t: T) {
  return z.union([
    // Null -> undefined
    z.null().transform(() => unrecognized(undefined)),
    t,
  ]).optional();
}

export function nullable<T extends z.ZodType>(t: T) {
  return z.union([
    z.null(),

    // Undefined -> null
    z.undefined().transform(() => defaultToZeroValue(null)),
    t,
  ]);
}

function zodDefaultToZeroValue<T>(
  value: T,
): z.ZodType<T, z.ZodTypeDef, unknown> {
  return z.any().transform((input, ctx) => {
    if (input === undefined) return defaultToZeroValue(value);
    if (input === null) return defaultToZeroValue(value);
    ctx.addIssue({
      code: "invalid_type",
      expected: "undefined",
      received: "unknown",
    });
    return z.NEVER;
  });
}
