import { TypeOrmPaymentsSchemaError } from '../errors/TypeOrmPaymentsSchemaError';

export function assertNonEmptyString(
  entityName: string,
  fieldName: string,
  value: unknown,
): asserts value is string {
  if (typeof value !== 'string') {
    throw new TypeOrmPaymentsSchemaError({ entityName, fieldName, reason: 'not_string' });
  }

  if (value.trim().length === 0) {
    throw new TypeOrmPaymentsSchemaError({ entityName, fieldName, reason: 'empty_string' });
  }
}

export function assertNumber(entityName: string, fieldName: string, value: unknown): asserts value is number {
  if (typeof value !== 'number' || Number.isNaN(value)) {
    throw new TypeOrmPaymentsSchemaError({ entityName, fieldName, reason: 'not_number' });
  }
}

export function assertInteger(entityName: string, fieldName: string, value: unknown): asserts value is number {
  if (typeof value !== 'number' || !Number.isInteger(value)) {
    throw new TypeOrmPaymentsSchemaError({ entityName, fieldName, reason: 'not_integer' });
  }
}

export function assertBoolean(entityName: string, fieldName: string, value: unknown): asserts value is boolean {
  if (typeof value !== 'boolean') {
    throw new TypeOrmPaymentsSchemaError({ entityName, fieldName, reason: 'not_boolean' });
  }
}

export function assertDate(entityName: string, fieldName: string, value: unknown): asserts value is Date {
  if (!(value instanceof Date)) {
    throw new TypeOrmPaymentsSchemaError({ entityName, fieldName, reason: 'not_date' });
  }
  if (Number.isNaN(value.getTime())) {
    throw new TypeOrmPaymentsSchemaError({ entityName, fieldName, reason: 'invalid_date' });
  }
}

export function assertEnumValue<TAllowed extends string>(
  entityName: string,
  fieldName: string,
  value: unknown,
  allowed: readonly TAllowed[],
): asserts value is TAllowed {
  if (typeof value !== 'string') {
    throw new TypeOrmPaymentsSchemaError({ entityName, fieldName, reason: 'not_string' });
  }

  if (!allowed.includes(value as TAllowed)) {
    throw new TypeOrmPaymentsSchemaError({ entityName, fieldName, reason: 'invalid_enum_value' });
  }
}

export function assertOptionalStringOrNull(
  entityName: string,
  fieldName: string,
  value: unknown,
): asserts value is string | null | undefined {
  if (value === null || value === undefined) {
    return;
  }

  if (typeof value !== 'string') {
    throw new TypeOrmPaymentsSchemaError({ entityName, fieldName, reason: 'not_string' });
  }
}

export function assertOptionalDate(
  entityName: string,
  fieldName: string,
  value: unknown,
): asserts value is Date | undefined | null {
  if (value === undefined || value === null) {
    return;
  }

  assertDate(entityName, fieldName, value);
}

export function assertOptionalNumber(
  entityName: string,
  fieldName: string,
  value: unknown,
): asserts value is number | undefined | null {
  if (value === undefined || value === null) {
    return;
  }

  assertNumber(entityName, fieldName, value);
}

export function assertOptionalBoolean(
  entityName: string,
  fieldName: string,
  value: unknown,
): asserts value is boolean | undefined | null {
  if (value === undefined || value === null) {
    return;
  }

  assertBoolean(entityName, fieldName, value);
}

export const TypeOrmPaymentsSchemaGuards = {
  assertNonEmptyString,
  assertNumber,
  assertInteger,
  assertBoolean,
  assertDate,
  assertEnumValue,
  assertOptionalStringOrNull,
  assertOptionalDate,
  assertOptionalNumber,
  assertOptionalBoolean,
} as const;