/* eslint-disable @typescript-eslint/no-explicit-any */
/**
 * Casts specified properties of an object to the desired types by specifying keys and their corresponding desired type.
 *
 * @param obj The object whose properties need to be cast
 * @param typeMappings An object where keys are property names from the source object
 *                     and values are the type to cast to ('string', 'number', 'boolean', etc.)
 * @returns A new object with the specified properties cast to their desired types
 */
function typeCaster<T extends object, K extends keyof T>(
  obj: T,
  typeMappings: Partial<
    Record<K, "string" | "number" | "boolean" | "object" | "array">
  >,
): T {
  const result = { ...obj };

  for (const key in typeMappings) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
      const targetType = typeMappings[key];
      const value = obj[key];

      switch (targetType) {
        case "string":
          (result as any)[key] = String(value);
          break;
        case "number":
          (result as any)[key] = Number(value);
          break;
        case "boolean":
          (result as any)[key] = Boolean(value);
          break;
        case "object":
          if (value && typeof value !== "object") {
            try {
              (result as any)[key] = JSON.parse(String(value));
            } catch {
              (result as any)[key] = {};
            }
          }
          break;
        case "array":
          if (Array.isArray(value)) {
            (result as any)[key] = value;
          } else if (value && typeof value === "string") {
            try {
              const parsed = JSON.parse(value);
              (result as any)[key] = Array.isArray(parsed) ? parsed : [parsed];
            } catch {
              (result as any)[key] = [value];
            }
          } else {
            (result as any)[key] = value ? [value] : [];
          }
          break;
      }
    }
  }

  return result;
}

export default typeCaster;
