// Type checkers
export const isNumber = (value: unknown): value is number => {
  return typeof value === 'number';
};

export const isValidFiniteNumber = (value: unknown): value is number => {
  return typeof value === 'number' && isFinite(value);
};

export const isNonNegativeFiniteNumber = (value: unknown): value is number => {
  return typeof value === 'number' && isFinite(value) && value >= 0;
};

export const isBoolean = (value: unknown): value is boolean => {
  return typeof value === 'boolean';
};

export const isString = (value: unknown): value is string => {
  return typeof value === 'string';
};

export const isObject = (value: unknown): value is Record<string, unknown> => {
  return typeof value === 'object' && value !== null && !Array.isArray(value);
};

export const isArray = (value: unknown): value is unknown[] => {
  return Array.isArray(value);
};

// Fixer factory
export const makeFixer = <T>(
  typeCheck: (value: unknown) => boolean,
  defaultValue: T
): ((value: unknown) => T) => {
  return (value: unknown): T => {
    if (typeCheck(value)) {
      return value as T;
    }
    return defaultValue;
  };
};

// Optional value fixers
export const fixOptionalString = (s: unknown): string | undefined => {
  if (s === undefined) {
    return undefined;
  }
  if (isString(s)) {
    return s;
  }
  return undefined;
};

export const fixOptionalNumber = (n: unknown): number | undefined => {
  if (n === undefined) {
    return undefined;
  }
  if (isValidFiniteNumber(n)) {
    return n;
  }
  return undefined;
};

export const fixOptionalBoolean = (b: unknown): boolean | undefined => {
  if (b === undefined) {
    return undefined;
  }
  if (isBoolean(b)) {
    return b;
  }
  return undefined;
};

// Array helper: fix and filter array in place, maintaining referential stability
export const fixArrayInPlace = <T>(
  arr: unknown[],
  fixer: (item: unknown) => T | null
): void => {
  let writeIndex = 0;
  for (let i = 0; i < arr.length; i++) {
    const fixed = fixer(arr[i]);
    if (fixed !== null) {
      arr[writeIndex] = fixed;
      writeIndex++;
    }
  }
  arr.length = writeIndex;
};
