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

part of '../sqlite_repositories.dart';

/// Returns a typed mutation failure.
core.RepositoryMutationResult _mutationFailure(
  core.RepositoryFailureCode code, {
  String? entityId,
  int? expectedRevision,
  int? actualRevision,
}) {
  return core.RepositoryMutationResult.failure(
    core.RepositoryFailure(
      code: code,
      entityId: entityId,
      expectedRevision: expectedRevision,
      actualRevision: actualRevision,
    ),
  );
}

/// Compares row ownership and revision before a write.
core.RepositoryMutationResult? _compareForWrite({
  required Object? row,
  required String ownerId,
  required int expectedRevision,
  required String entityId,
  required String? rowOwnerId,
  required int? rowRevision,
}) {
  if (expectedRevision <= 0) {
    return _mutationFailure(
      core.RepositoryFailureCode.invalidRevision,
      entityId: entityId,
      expectedRevision: expectedRevision,
    );
  }
  if (row == null) {
    return _mutationFailure(
      core.RepositoryFailureCode.notFound,
      entityId: entityId,
    );
  }
  if (rowOwnerId != ownerId) {
    return _mutationFailure(
      core.RepositoryFailureCode.ownerMismatch,
      entityId: entityId,
    );
  }
  if (rowRevision != expectedRevision) {
    return _mutationFailure(
      core.RepositoryFailureCode.staleRevision,
      entityId: entityId,
      expectedRevision: expectedRevision,
      actualRevision: rowRevision,
    );
  }
  return null;
}

/// Converts a cursor string to a bounded integer offset.
int _applicationCursorOffset(String? cursor) {
  if (cursor == null) {
    return 0;
  }
  final parsed = int.tryParse(cursor);
  if (parsed == null || parsed < 0) {
    return 0;
  }
  return parsed;
}

/// Builds a repository page from already sorted [items].
core.RepositoryPage<T> _applicationPage<T>(
  List<T> items,
  core.RepositoryPageRequest request,
) {
  final offset = _applicationCursorOffset(request.cursor);
  final safeOffset = offset.clamp(0, items.length);
  final end = (safeOffset + request.limit).clamp(0, items.length);
  final pageItems = items.sublist(safeOffset, end);
  return core.RepositoryPage<T>(
    items: List<T>.unmodifiable(pageItems),
    nextCursor: end < items.length ? '$end' : null,
  );
}

/// Builds a repository page from Drift [rows] fetched with one extra row.
core.RepositoryPage<T> _applicationPageFromRows<R, T>(
  List<R> rows,
  core.RepositoryPageRequest request,
  T Function(R row) convert,
) {
  final pageRows = rows.take(request.limit).toList(growable: false);
  return core.RepositoryPage<T>(
    items: List<T>.unmodifiable(pageRows.map(convert)),
    nextCursor: rows.length > request.limit
        ? '${_applicationCursorOffset(request.cursor) + request.limit}'
        : null,
  );
}

/// Returns a stored UTC instant as a [DateTime], if present.
DateTime? _storedDateTime(DateTime? value) {
  return value?.toUtc();
}

/// Encodes metadata values to JSON-compatible values.
String _metadataJson(Map<String, Object?> metadata) {
  return jsonEncode(_jsonSafe(metadata));
}

/// Decodes JSON metadata from SQLite.
Map<String, Object?> _metadataFromJson(String value) {
  return Map<String, Object?>.from(
    jsonDecode(value) as Map<dynamic, dynamic>,
  );
}

/// Converts [value] to values supported by `jsonEncode`.
Object? _jsonSafe(Object? value) {
  if (value == null || value is String || value is num || value is bool) {
    return value;
  }
  if (value is DateTime) {
    return core.PersistenceDateTimeConvention.toStoredString(value);
  }
  if (value is Enum) {
    return value.name;
  }
  if (value is Map) {
    return {
      for (final entry in value.entries)
        entry.key.toString(): _jsonSafe(entry.value as Object?),
    };
  }
  if (value is Iterable) {
    return [for (final item in value) _jsonSafe(item as Object?)];
  }
  return value.toString();
}

/// Encodes integer-keyed counts.
String _intCountMapJson(Map<int, int> counts) {
  return jsonEncode({
    for (final entry in counts.entries) '${entry.key}': entry.value,
  });
}

/// Decodes integer-keyed counts.
Map<int, int> _intCountMap(String value) {
  final decoded = jsonDecode(value) as Map<dynamic, dynamic>;
  return {
    for (final entry in decoded.entries)
      int.parse(entry.key as String): entry.value as int,
  };
}

/// Encodes enum-keyed counts.
String _enumCountMapJson<T extends Enum>(Map<T, int> counts) {
  return jsonEncode({
    for (final entry in counts.entries) entry.key.name: entry.value,
  });
}

/// Decodes enum-keyed counts.
Map<T, int> _enumCountMap<T extends Enum>(
  String value,
  List<T> values,
) {
  final decoded = jsonDecode(value) as Map<dynamic, dynamic>;
  return {
    for (final entry in decoded.entries)
      _enumByName(values, entry.key as String): entry.value as int,
  };
}

/// Returns an enum value by stable [name].
T _enumByName<T extends Enum>(List<T> values, String name) {
  return values.firstWhere((value) => value.name == name);
}
