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

import * as z from "zod/v4";
import { startCountingUnrecognized } from "./unrecognized.js";

const UNKNOWN = Symbol("UNKNOWN");

export type Unknown<Discriminator extends string, UnknownValue = "UNKNOWN"> =
  & {
    [K in Discriminator]: UnknownValue;
  }
  & {
    raw: unknown;
    isUnknown: true;
  };

export function isUnknown<Discriminator extends string>(
  value: unknown,
): value is Unknown<Discriminator> {
  return typeof value === "object" && value !== null && UNKNOWN in value;
}

/**
 * Forward-compatible discriminated union parser.
 *
 * If the input does not match one of the predefined options, it will be
 * captured and available as `{ raw: <original input>, [discriminator]: "UNKNOWN", isUnknown: true }`.
 *
 * @param inputPropertyName - The discriminator property name in the input payload
 * @param options - Map of discriminator values to their corresponding schemas
 * @param opts - Optional configuration object
 * @param opts.unknownValue - The value to use for the discriminator when the input is unknown (default: "UNKNOWN")
 * @param opts.outputPropertyName - Output property name if the sanitized (camelCase) property name differs from inputPropertyName
 */
export function discriminatedUnion<
  InputDiscriminator extends string,
  TOptions extends Readonly<Record<string, z.ZodType>>,
  UnknownValue extends string = "UNKNOWN",
  OutputDiscriminator extends string = InputDiscriminator,
>(
  inputPropertyName: InputDiscriminator,
  options: TOptions,
  opts: {
    unknownValue?: UnknownValue;
    outputPropertyName?: OutputDiscriminator;
  } = {},
): z.ZodType<
  | z.output<TOptions[keyof TOptions]>
  | Unknown<OutputDiscriminator, UnknownValue>,
  unknown
> {
  const { unknownValue = "UNKNOWN" as UnknownValue, outputPropertyName } = opts;
  return z.unknown().transform((input) => {
    const fallback = Object.defineProperties(
      {
        raw: input,
        [outputPropertyName ?? inputPropertyName]: unknownValue,
        isUnknown: true as const,
      },
      { [UNKNOWN]: { value: true, enumerable: false, configurable: false } },
    );

    const isObject = typeof input === "object" && input !== null;
    if (!isObject) return fallback;

    const discriminator = input[inputPropertyName as keyof typeof input];
    if (typeof discriminator !== "string") return fallback;
    if (!(discriminator in options)) return fallback;

    const schema = options[discriminator];
    if (!schema) return fallback;

    // Start counters before parsing to track nested unrecognized/zeroDefault values
    const unrecognizedCtr = startCountingUnrecognized();

    const result = schema.safeParse(input);
    if (!result.success) {
      // Parse failed - don't propagate any counts from the failed attempt
      unrecognizedCtr.end(0);

      return fallback;
    }

    // Parse succeeded - propagate the actual counts
    unrecognizedCtr.end();

    if (outputPropertyName) {
      (result.data as any)[outputPropertyName] = discriminator;
    }

    return result.data;
  }) as any;
}
