import { Type } from 'class-transformer';

/**
 * Makes all properties of T optional
 * Similar to NestJS mapped-types PartialType but for plain classes
 */
export function PartialType<T extends new (...args: any[]) => any>(classRef: T): T {
  abstract class PartialTypeClass {
    constructor() {
      return Object.assign(this, new classRef());
    }
  }

  const propertyKeys = Object.getOwnPropertyNames(new classRef());
  propertyKeys.forEach((key) => {
    const decorators = Reflect.getMetadataKeys(classRef.prototype, key);
    decorators.forEach((decoratorKey) => {
      const metadata = Reflect.getMetadata(decoratorKey, classRef.prototype, key);
      Reflect.defineMetadata(decoratorKey, metadata, PartialTypeClass.prototype, key);
    });
  });

  return PartialTypeClass as T;
}

/**
 * Type helper for partial types
 */
export type DeepPartial<T> = {
  [P in keyof T]?: T[P] extends Array<infer U>
    ? Array<DeepPartial<U>>
    : T[P] extends ReadonlyArray<infer U>
    ? ReadonlyArray<DeepPartial<U>>
    : DeepPartial<T[P]>;
};
