// SPDX-FileCopyrightText: 2026 FocusFlow contributors
// SPDX-License-Identifier: AGPL-3.0-only

part of '../../document_mapping.dart';

/// Convenience extension for converting [ProjectStatistics] into a document.
extension ProjectStatisticsDocumentExtension on ProjectStatistics {
  /// Converts scheduler data for `toDocument` without changing the source object.
  /// Conversion helpers keep persistence and export shapes explicit so domain models do not depend on storage-specific details.
  Map<String, Object?> toDocument({
    required String ownerId,
    required DateTime createdAt,
    required DateTime updatedAt,
    int revision = 1,
    Iterable<String> appliedActivityIds = const <String>[],
  }) {
    return ProjectStatisticsDocumentMapping.toDocument(
      this,
      ownerId: ownerId,
      createdAt: createdAt,
      updatedAt: updatedAt,
      revision: revision,
      appliedActivityIds: appliedActivityIds,
    );
  }
}

/// Private file-level value for `_taskTypeByCode`.
/// Keeping this value near the helpers that use it avoids hidden global configuration while still preventing duplicate literals.
const _taskTypeByCode = <String, TaskType>{
  'flexible': TaskType.flexible,
  'inflexible': TaskType.inflexible,
  'critical': TaskType.critical,
  'locked': TaskType.locked,
  'surprise': TaskType.surprise,
  'free_slot': TaskType.freeSlot,
};

/// Private file-level value for `_taskStatusByCode`.
/// Keeping this value near the helpers that use it avoids hidden global configuration while still preventing duplicate literals.
const _taskStatusByCode = <String, TaskStatus>{
  'planned': TaskStatus.planned,
  'active': TaskStatus.active,
  'completed': TaskStatus.completed,
  'missed': TaskStatus.missed,
  'cancelled': TaskStatus.cancelled,
  'no_longer_relevant': TaskStatus.noLongerRelevant,
  'backlog': TaskStatus.backlog,
};

/// Private file-level value for `_priorityByCode`.
/// Keeping this value near the helpers that use it avoids hidden global configuration while still preventing duplicate literals.
const _priorityByCode = <String, PriorityLevel>{
  'very_low': PriorityLevel.veryLow,
  'low': PriorityLevel.low,
  'medium': PriorityLevel.medium,
  'high': PriorityLevel.high,
  'very_high': PriorityLevel.veryHigh,
};

/// Private file-level value for `_rewardByCode`.
/// Keeping this value near the helpers that use it avoids hidden global configuration while still preventing duplicate literals.
const _rewardByCode = <String, RewardLevel>{
  'not_set': RewardLevel.notSet,
  'very_low': RewardLevel.veryLow,
  'low': RewardLevel.low,
  'medium': RewardLevel.medium,
  'high': RewardLevel.high,
  'very_high': RewardLevel.veryHigh,
};

/// Private file-level value for `_difficultyByCode`.
/// Keeping this value near the helpers that use it avoids hidden global configuration while still preventing duplicate literals.
const _difficultyByCode = <String, DifficultyLevel>{
  'not_set': DifficultyLevel.notSet,
  'very_easy': DifficultyLevel.veryEasy,
  'easy': DifficultyLevel.easy,
  'medium': DifficultyLevel.medium,
  'hard': DifficultyLevel.hard,
  'very_hard': DifficultyLevel.veryHard,
};

/// Private file-level value for `_reminderProfileByCode`.
/// Keeping this value near the helpers that use it avoids hidden global configuration while still preventing duplicate literals.
const _reminderProfileByCode = <String, ReminderProfile>{
  'silent': ReminderProfile.silent,
  'gentle': ReminderProfile.gentle,
  'persistent': ReminderProfile.persistent,
  'strict': ReminderProfile.strict,
};

/// Private file-level value for `_backlogTagByCode`.
/// Keeping this value near the helpers that use it avoids hidden global configuration while still preventing duplicate literals.
const _backlogTagByCode = <String, BacklogTag>{
  'wishlist': BacklogTag.wishlist,
};

/// Private file-level value for `_lockedWeekdayByCode`.
/// Keeping this value near the helpers that use it avoids hidden global configuration while still preventing duplicate literals.
const _lockedWeekdayByCode = <String, LockedWeekday>{
  'monday': LockedWeekday.monday,
  'tuesday': LockedWeekday.tuesday,
  'wednesday': LockedWeekday.wednesday,
  'thursday': LockedWeekday.thursday,
  'friday': LockedWeekday.friday,
  'saturday': LockedWeekday.saturday,
  'sunday': LockedWeekday.sunday,
};

/// Private file-level value for `_lockedBlockOverrideTypeByCode`.
/// Keeping this value near the helpers that use it avoids hidden global configuration while still preventing duplicate literals.
const _lockedBlockOverrideTypeByCode = <String, LockedBlockOverrideType>{
  'remove': LockedBlockOverrideType.remove,
  'replace': LockedBlockOverrideType.replace,
  'add': LockedBlockOverrideType.add,
};

/// Private file-level value for `_taskActivityCodeByCode`.
/// Keeping this value near the helpers that use it avoids hidden global configuration while still preventing duplicate literals.
const _taskActivityCodeByCode = <String, TaskActivityCode>{
  'completed': TaskActivityCode.completed,
  'missed': TaskActivityCode.missed,
  'cancelled': TaskActivityCode.cancelled,
  'no_longer_relevant': TaskActivityCode.noLongerRelevant,
  'manually_pushed': TaskActivityCode.manuallyPushed,
  'automatically_pushed': TaskActivityCode.automaticallyPushed,
  'moved_to_backlog': TaskActivityCode.movedToBacklog,
  'restored_from_backlog': TaskActivityCode.restoredFromBacklog,
  'activated': TaskActivityCode.activated,
};

/// Private file-level value for `_schedulingNoticeTypeByCode`.
/// Keeping this value near the helpers that use it avoids hidden global configuration while still preventing duplicate literals.
const _schedulingNoticeTypeByCode = <String, SchedulingNoticeType>{
  'info': SchedulingNoticeType.info,
  'moved': SchedulingNoticeType.moved,
  'overlap': SchedulingNoticeType.overlap,
  'no_fit': SchedulingNoticeType.noFit,
  'overflow': SchedulingNoticeType.overflow,
};

/// Private file-level value for `_schedulingIssueCodeByCode`.
/// Keeping this value near the helpers that use it avoids hidden global configuration while still preventing duplicate literals.
const _schedulingIssueCodeByCode = <String, SchedulingIssueCode>{
  'task_not_found': SchedulingIssueCode.taskNotFound,
  'invalid_task_state': SchedulingIssueCode.invalidTaskState,
  'missing_duration': SchedulingIssueCode.missingDuration,
  'missing_scheduled_slot': SchedulingIssueCode.missingScheduledSlot,
  'non_positive_duration': SchedulingIssueCode.nonPositiveDuration,
  'no_available_slot': SchedulingIssueCode.noAvailableSlot,
  'unfinished_tasks_could_not_fit':
      SchedulingIssueCode.unfinishedTasksCouldNotFit,
  'no_unfinished_flexible_tasks': SchedulingIssueCode.noUnfinishedFlexibleTasks,
  'duplicate_surprise_log': SchedulingIssueCode.duplicateSurpriseLog,
};

/// Private file-level value for `_schedulingMovementCodeByCode`.
/// Keeping this value near the helpers that use it avoids hidden global configuration while still preventing duplicate literals.
const _schedulingMovementCodeByCode = <String, SchedulingMovementCode>{
  'backlog_task_inserted': SchedulingMovementCode.backlogTaskInserted,
  'flexible_task_moved_to_make_room':
      SchedulingMovementCode.flexibleTaskMovedToMakeRoom,
  'flexible_task_pushed_to_next_available_slot':
      SchedulingMovementCode.flexibleTaskPushedToNextAvailableSlot,
  'flexible_task_moved_to_tomorrow':
      SchedulingMovementCode.flexibleTaskMovedToTomorrow,
  'unfinished_flexible_tasks_rolled_over':
      SchedulingMovementCode.unfinishedFlexibleTasksRolledOver,
  'flexible_task_moved_to_backlog':
      SchedulingMovementCode.flexibleTaskMovedToBacklog,
  'required_commitment_scheduled':
      SchedulingMovementCode.requiredCommitmentScheduled,
};

/// Private file-level value for `_schedulingConflictCodeByCode`.
/// Keeping this value near the helpers that use it avoids hidden global configuration while still preventing duplicate literals.
const _schedulingConflictCodeByCode = <String, SchedulingConflictCode>{
  'flexible_task_overlaps_blocked_time':
      SchedulingConflictCode.flexibleTaskOverlapsBlockedTime,
  'surprise_task_overlaps_required_visible_time':
      SchedulingConflictCode.surpriseTaskOverlapsRequiredVisibleTime,
  'required_commitment_overlaps_protected_free_slot':
      SchedulingConflictCode.requiredCommitmentOverlapsProtectedFreeSlot,
};

/// Private file-level value for `_projectCompletionTimeBucketByCode`.
/// Keeping this value near the helpers that use it avoids hidden global configuration while still preventing duplicate literals.
const _projectCompletionTimeBucketByCode =
    <String, ProjectCompletionTimeBucket>{
  'overnight': ProjectCompletionTimeBucket.overnight,
  'morning': ProjectCompletionTimeBucket.morning,
  'afternoon': ProjectCompletionTimeBucket.afternoon,
  'evening': ProjectCompletionTimeBucket.evening,
  'night': ProjectCompletionTimeBucket.night,
};

/// Top-level helper that performs the `_decodeCode<T>` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
T _decodeCode<T>(Map<String, T> valuesByCode, String code, String fieldName) {
  final value = valuesByCode[code];
  if (value == null) {
    throw DocumentMappingException(
      code: DocumentMappingFailureCode.unknownCode,
      fieldName: fieldName,
      detailCode: code,
    );
  }
  return value;
}

/// Top-level helper that performs the `_commonFields` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
Map<String, Object?> _commonFields({
  required String id,
  required String ownerId,
  required int revision,
  required DateTime createdAt,
  required DateTime updatedAt,
}) {
  if (revision <= 0) {
    throw const DocumentMappingException(
      code: DocumentMappingFailureCode.invalidRevision,
      fieldName: DocumentFields.revision,
    );
  }
  return {
    DocumentFields.schemaVersion: v1SchemaVersion,
    DocumentFields.id: id,
    DocumentFields.ownerId: ownerId,
    DocumentFields.revision: revision,
    DocumentFields.createdAt:
        PersistenceDateTimeConvention.toStoredString(createdAt),
    DocumentFields.updatedAt:
        PersistenceDateTimeConvention.toStoredString(updatedAt),
  };
}

/// Top-level helper that performs the `_readCommon` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
DocumentMetadata _readCommon(Map<String, Object?> document) {
  final schemaVersion = _requiredInt(document, DocumentFields.schemaVersion);
  if (schemaVersion != v1SchemaVersion) {
    throw DocumentMappingException(
      code: DocumentMappingFailureCode.invalidSchemaVersion,
      fieldName: DocumentFields.schemaVersion,
      detailCode: schemaVersion.toString(),
    );
  }
  final revision = _requiredInt(document, DocumentFields.revision);
  if (revision <= 0) {
    throw DocumentMappingException(
      code: DocumentMappingFailureCode.invalidRevision,
      fieldName: DocumentFields.revision,
      detailCode: revision.toString(),
    );
  }
  return DocumentMetadata(
    id: _requiredString(document, DocumentFields.id),
    ownerId: _requiredString(document, DocumentFields.ownerId),
    revision: revision,
    createdAt: _requiredDateTime(document, DocumentFields.createdAt),
    updatedAt: _requiredDateTime(document, DocumentFields.updatedAt),
  );
}

/// Top-level helper that performs the `_mapInvalidValues<T>` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
T _mapInvalidValues<T>(T Function() read) {
  try {
    return read();
  } on DocumentMappingException {
    rethrow;
  } on DomainValidationException catch (error) {
    throw DocumentMappingException(
      code: DocumentMappingFailureCode.invalidValue,
      fieldName: error.name,
      detailCode: error.code.name,
    );
  } on FormatException catch (error) {
    throw DocumentMappingException(
      code: DocumentMappingFailureCode.invalidValue,
      detailCode: error.message,
    );
  } on ArgumentError catch (error) {
    throw DocumentMappingException(
      code: DocumentMappingFailureCode.invalidValue,
      fieldName: error.name,
    );
  }
}

/// Top-level helper that performs the `_dateTimeToStored` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
String? _dateTimeToStored(DateTime? value) {
  return value == null
      ? null
      : PersistenceDateTimeConvention.toStoredString(value);
}

/// Top-level helper that performs the `_civilDateToStored` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
String? _civilDateToStored(CivilDate? value) {
  return value == null
      ? null
      : PersistenceCivilDateConvention.toStoredString(value);
}

/// Top-level helper that performs the `_wallTimeToStored` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
String? _wallTimeToStored(WallTime? value) {
  return value == null
      ? null
      : PersistenceWallTimeConvention.toStoredString(value);
}

/// Top-level helper that performs the `_requiredString` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
String _requiredString(Map<String, Object?> document, String fieldName) {
  if (!document.containsKey(fieldName)) {
    throw DocumentMappingException(
      code: DocumentMappingFailureCode.missingField,
      fieldName: fieldName,
    );
  }
  final value = document[fieldName];
  if (value is String) {
    return value;
  }
  throw DocumentMappingException(
    code: DocumentMappingFailureCode.wrongType,
    fieldName: fieldName,
  );
}

/// Top-level helper that performs the `_requiredNullableString` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
String? _requiredNullableString(
  Map<String, Object?> document,
  String fieldName,
) {
  if (!document.containsKey(fieldName)) {
    throw DocumentMappingException(
      code: DocumentMappingFailureCode.missingField,
      fieldName: fieldName,
    );
  }
  final value = document[fieldName];
  if (value == null || value is String) {
    return value as String?;
  }
  throw DocumentMappingException(
    code: DocumentMappingFailureCode.wrongType,
    fieldName: fieldName,
  );
}

/// Top-level helper that performs the `_requiredInt` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
int _requiredInt(Map<String, Object?> document, String fieldName) {
  if (!document.containsKey(fieldName)) {
    throw DocumentMappingException(
      code: DocumentMappingFailureCode.missingField,
      fieldName: fieldName,
    );
  }
  final value = document[fieldName];
  if (value is int) {
    return value;
  }
  throw DocumentMappingException(
    code: DocumentMappingFailureCode.wrongType,
    fieldName: fieldName,
  );
}

/// Top-level helper that performs the `_requiredNullableInt` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
int? _requiredNullableInt(
  Map<String, Object?> document,
  String fieldName,
) {
  if (!document.containsKey(fieldName)) {
    throw DocumentMappingException(
      code: DocumentMappingFailureCode.missingField,
      fieldName: fieldName,
    );
  }
  final value = document[fieldName];
  if (value == null || value is int) {
    return value as int?;
  }
  throw DocumentMappingException(
    code: DocumentMappingFailureCode.wrongType,
    fieldName: fieldName,
  );
}

/// Top-level helper that performs the `_requiredBool` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
bool _requiredBool(Map<String, Object?> document, String fieldName) {
  if (!document.containsKey(fieldName)) {
    throw DocumentMappingException(
      code: DocumentMappingFailureCode.missingField,
      fieldName: fieldName,
    );
  }
  final value = document[fieldName];
  if (value is bool) {
    return value;
  }
  throw DocumentMappingException(
    code: DocumentMappingFailureCode.wrongType,
    fieldName: fieldName,
  );
}

/// Top-level helper that performs the `_requiredDateTime` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
DateTime _requiredDateTime(Map<String, Object?> document, String fieldName) {
  return PersistenceDateTimeConvention.fromStoredString(
    _requiredString(document, fieldName),
  );
}

/// Top-level helper that performs the `_requiredNullableDateTime` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
DateTime? _requiredNullableDateTime(
  Map<String, Object?> document,
  String fieldName,
) {
  final value = _requiredNullableString(document, fieldName);
  return value == null
      ? null
      : PersistenceDateTimeConvention.fromStoredString(value);
}

/// Top-level helper that performs the `_requiredCivilDate` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
CivilDate _requiredCivilDate(Map<String, Object?> document, String fieldName) {
  return PersistenceCivilDateConvention.fromStoredString(
    _requiredString(document, fieldName),
  );
}

/// Top-level helper that performs the `_requiredNullableCivilDate` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
CivilDate? _requiredNullableCivilDate(
  Map<String, Object?> document,
  String fieldName,
) {
  final value = _requiredNullableString(document, fieldName);
  return value == null
      ? null
      : PersistenceCivilDateConvention.fromStoredString(value);
}

/// Top-level helper that performs the `_requiredWallTime` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
WallTime _requiredWallTime(Map<String, Object?> document, String fieldName) {
  return PersistenceWallTimeConvention.fromStoredString(
    _requiredString(document, fieldName),
  );
}

/// Top-level helper that performs the `_requiredNullableWallTime` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
WallTime? _requiredNullableWallTime(
  Map<String, Object?> document,
  String fieldName,
) {
  final value = _requiredNullableString(document, fieldName);
  return value == null
      ? null
      : PersistenceWallTimeConvention.fromStoredString(value);
}

/// Top-level helper that performs the `_requiredMap` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
Map<String, Object?> _requiredMap(
  Map<String, Object?> document,
  String fieldName,
) {
  if (!document.containsKey(fieldName)) {
    throw DocumentMappingException(
      code: DocumentMappingFailureCode.missingField,
      fieldName: fieldName,
    );
  }
  final value = document[fieldName];
  if (value is Map<String, Object?>) {
    return value;
  }
  if (value is Map) {
    return Map<String, Object?>.from(value);
  }
  throw DocumentMappingException(
    code: DocumentMappingFailureCode.wrongType,
    fieldName: fieldName,
  );
}

/// Top-level helper that performs the `_requiredNullableMap` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
Map<String, Object?>? _requiredNullableMap(
  Map<String, Object?> document,
  String fieldName,
) {
  if (!document.containsKey(fieldName)) {
    throw DocumentMappingException(
      code: DocumentMappingFailureCode.missingField,
      fieldName: fieldName,
    );
  }
  final value = document[fieldName];
  if (value == null) {
    return null;
  }
  if (value is Map<String, Object?>) {
    return value;
  }
  if (value is Map) {
    return Map<String, Object?>.from(value);
  }
  throw DocumentMappingException(
    code: DocumentMappingFailureCode.wrongType,
    fieldName: fieldName,
  );
}

/// Top-level helper that performs the `_requiredList` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
List<Object?> _requiredList(Map<String, Object?> document, String fieldName) {
  if (!document.containsKey(fieldName)) {
    throw DocumentMappingException(
      code: DocumentMappingFailureCode.missingField,
      fieldName: fieldName,
    );
  }
  final value = document[fieldName];
  if (value is List) {
    return List<Object?>.unmodifiable(value);
  }
  throw DocumentMappingException(
    code: DocumentMappingFailureCode.wrongType,
    fieldName: fieldName,
  );
}

/// Top-level helper that performs the `_mappedList<T>` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
List<T> _mappedList<T>(
  Map<String, Object?> document,
  String fieldName,
  T Function(Map<String, Object?> document) decode,
) {
  return _requiredList(document, fieldName).map((value) {
    if (value is Map<String, Object?>) {
      return decode(value);
    }
    if (value is Map) {
      return decode(Map<String, Object?>.from(value));
    }
    throw DocumentMappingException(
      code: DocumentMappingFailureCode.wrongType,
      fieldName: fieldName,
    );
  }).toList(growable: false);
}

/// Top-level helper that performs the `_intKeyCountMapToDocument` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
Map<String, Object?> _intKeyCountMapToDocument(Map<int, int> counts) {
  return {
    for (final entry in counts.entries) entry.key.toString(): entry.value,
  };
}

/// Runs the `Enum>` top-level operation.
/// This function is part of the file-level API and gives callers a direct way to use the associated scheduler behavior.
Map<String, Object?> _enumCountMapToDocument<T extends Enum>(
  Map<T, int> counts,
  String Function(T value) encode,
) {
  return {
    for (final entry in counts.entries) encode(entry.key): entry.value,
  };
}

/// Top-level helper that performs the `_intKeyCountMapFromDocument` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
Map<int, int> _intKeyCountMapFromDocument(
  Map<String, Object?> document,
  String fieldName,
) {
  final raw = _requiredMap(document, fieldName);
  return {
    for (final entry in raw.entries)
      int.parse(entry.key): _countValue(entry.value, fieldName),
  };
}

/// Runs the `Enum>` top-level operation.
/// This function is part of the file-level API and gives callers a direct way to use the associated scheduler behavior.
Map<T, int> _enumCountMapFromDocument<T extends Enum>(
  Map<String, Object?> document,
  String fieldName,
  T Function(String value) decode,
) {
  final raw = _requiredMap(document, fieldName);
  return {
    for (final entry in raw.entries)
      decode(entry.key): _countValue(entry.value, fieldName),
  };
}

/// Top-level helper that performs the `_countValue` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
int _countValue(Object? value, String fieldName) {
  if (value is int) {
    return value;
  }
  throw DocumentMappingException(
    code: DocumentMappingFailureCode.wrongType,
    fieldName: fieldName,
  );
}

/// Top-level helper that performs the `_plainDocument` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
Map<String, Object?> _plainDocument(Map<String, Object?> value) {
  return Map<String, Object?>.unmodifiable(
    value.map((key, value) => MapEntry(key, _plainDocumentValue(value))),
  );
}

/// Top-level helper that performs the `_plainDocumentValue` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
Object? _plainDocumentValue(Object? value) {
  if (value == null ||
      value is String ||
      value is num ||
      value is bool ||
      value is DateTime) {
    return value is DateTime
        ? PersistenceDateTimeConvention.toStoredString(value)
        : value;
  }
  if (value is CivilDate) {
    return PersistenceCivilDateConvention.toStoredString(value);
  }
  if (value is WallTime) {
    return PersistenceWallTimeConvention.toStoredString(value);
  }
  if (value is Enum) {
    return PersistenceEnumMapping.encode(value);
  }
  if (value is List) {
    return value.map(_plainDocumentValue).toList(growable: false);
  }
  if (value is Map<String, Object?>) {
    return _plainDocument(value);
  }
  if (value is Map) {
    return _plainDocument(Map<String, Object?>.from(value));
  }
  throw DocumentMappingException(
    code: DocumentMappingFailureCode.wrongType,
    detailCode: value.runtimeType.toString(),
  );
}
