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

export function assertNonEmptyString(entityName: string, fieldName: string, value: unknown): asserts value is string {
  if (value === undefined || value === null) {
    throw new TypeOrmSocialSchemaError({ entityName, fieldName, reason: 'missing' });
  }
  if (typeof value !== 'string') {
    throw new TypeOrmSocialSchemaError({ entityName, fieldName, reason: 'not_string' });
  }
  if (value.trim().length === 0) {
    throw new TypeOrmSocialSchemaError({ entityName, fieldName, reason: 'empty_string' });
  }
}

export function assertOptionalStringOrNull(
  entityName: string,
  fieldName: string,
  value: unknown,
): asserts value is string | null | undefined {
  if (value === undefined || value === null) return;
  if (typeof value !== 'string') {
    throw new TypeOrmSocialSchemaError({ entityName, fieldName, reason: 'not_string' });
  }
}

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

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

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

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

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

export function assertDate(entityName: string, fieldName: string, value: unknown): asserts value is Date {
  if (value === undefined || value === null) {
    throw new TypeOrmSocialSchemaError({ entityName, fieldName, reason: 'missing' });
  }
  if (!(value instanceof Date)) {
    throw new TypeOrmSocialSchemaError({ entityName, fieldName, reason: 'not_date' });
  }
  if (Number.isNaN(value.getTime())) {
    throw new TypeOrmSocialSchemaError({ 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 (value === undefined || value === null) {
    throw new TypeOrmSocialSchemaError({ entityName, fieldName, reason: 'missing' });
  }
  if (typeof value !== 'string') {
    throw new TypeOrmSocialSchemaError({ entityName, fieldName, reason: 'not_string' });
  }
  if (!allowed.includes(value as TAllowed)) {
    throw new TypeOrmSocialSchemaError({ entityName, fieldName, reason: 'invalid_enum_value' });
  }
}

export const TypeOrmSocialSchemaGuards = {
  assertNonEmptyString,
  assertOptionalStringOrNull,
  assertNumber,
  assertOptionalNumberOrNull,
  assertInteger,
  assertOptionalIntegerOrNull,
  assertBoolean,
  assertDate,
  assertEnumValue,
};