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

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

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

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

  const parsed = new Date(value);
  if (Number.isNaN(parsed.getTime())) {
    throw new TypeOrmPersistenceSchemaError({ entityName, fieldName, reason: 'not_iso_date' });
  }

  if (parsed.toISOString() !== value) {
    throw new TypeOrmPersistenceSchemaError({ entityName, fieldName, reason: 'not_iso_date' });
  }
}

export function assertDate(entityName: string, fieldName: string, value: unknown): asserts value is Date {
  if (!(value instanceof Date)) {
    throw new TypeOrmPersistenceSchemaError({ entityName, fieldName, reason: 'not_date' });
  }
  if (Number.isNaN(value.getTime())) {
    throw new TypeOrmPersistenceSchemaError({ 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 TypeOrmPersistenceSchemaError({ entityName, fieldName, reason: 'not_string' });
  }

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

export function assertArray(entityName: string, fieldName: string, value: unknown): asserts value is unknown[] {
  if (!Array.isArray(value)) {
    throw new TypeOrmPersistenceSchemaError({ entityName, fieldName, reason: 'not_array' });
  }
}

export function assertNumber(entityName: string, fieldName: string, value: unknown): asserts value is number {
  if (typeof value !== 'number' || Number.isNaN(value)) {
    throw new TypeOrmPersistenceSchemaError({ 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 TypeOrmPersistenceSchemaError({ entityName, fieldName, reason: 'not_integer' });
  }
}

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

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

  assertNumber(entityName, fieldName, value);
}

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

  assertInteger(entityName, fieldName, value);
}

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

  assertBoolean(entityName, fieldName, value);
}

export function assertRecord(entityName: string, fieldName: string, value: unknown): asserts value is Record<string, unknown> {
  if (typeof value !== 'object' || value === null || Array.isArray(value)) {
    throw new TypeOrmPersistenceSchemaError({ entityName, fieldName, reason: 'not_object' });
  }
}

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 TypeOrmPersistenceSchemaError({ entityName, fieldName, reason: 'not_string' });
  }
}