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

part of '../../persistence_memory.dart';

/// In-memory diagnostic schedule snapshot repository.
final class InMemoryScheduleSnapshotRepository
    implements ScheduleSnapshotRepository {
  /// Private state stored as `_records` for this implementation.
  /// The field is intentionally scoped to this library so only the owning type can change the related scheduler or UI behavior.
  final Map<String, _SnapshotRecord> _records = <String, _SnapshotRecord>{};

  /// Runs the `findById` operation and completes after its asynchronous work finishes.
  /// Callers should await the returned future so repository writes, UI refreshes, and failure handling happen in a predictable order.
  @override
  Future<RepoResult<core.SchedulingStateSnapshot?>> findById({
    required core.OwnerId ownerId,
    required String snapshotId,
  }) async {
    final record = _records[snapshotId];
    if (record == null || record.ownerId != ownerId) {
      return const Right(null);
    }
    return Right(_cloneSnapshot(record.value, record.ownerId, record.revision));
  }

  /// Runs the `findInWindow` operation and completes after its asynchronous work finishes.
  /// Callers should await the returned future so repository writes, UI refreshes, and failure handling happen in a predictable order.
  @override
  Future<RepoResult<core.Page<core.SchedulingStateSnapshot>>> findInWindow({
    required core.OwnerId ownerId,
    required core.SchedulingWindow window,
    core.PageRequest page = const core.PageRequest(),
  }) async {
    return Right(_pageRecords(
      _records.values.where(
        (record) =>
            record.ownerId == ownerId &&
            _windowsOverlap(record.value.window, window),
      ),
      page,
      (record) => _cloneSnapshot(record.value, record.ownerId, record.revision),
    ));
  }

  /// Runs the `insert` operation and completes after its asynchronous work finishes.
  /// Callers should await the returned future so repository writes, UI refreshes, and failure handling happen in a predictable order.
  @override
  Future<RepoResult<core.Revision>> insert({
    required core.OwnerId ownerId,
    required core.SchedulingStateSnapshot snapshot,
  }) async {
    if (_records.containsKey(snapshot.id)) {
      return Left(RepositoryDuplicateId(entityId: snapshot.id));
    }
    _records[snapshot.id] = _SnapshotRecord(
      id: snapshot.id,
      ownerId: ownerId,
      value: _cloneSnapshot(snapshot, ownerId, core.Revision.initial),
      revision: core.Revision.initial,
      retentionExpiresAt: snapshot.capturedAt.add(_snapshotRetention),
    );
    return const Right(core.Revision.initial);
  }

  /// Runs the `deleteExpired` operation and completes after its asynchronous work finishes.
  /// Callers should await the returned future so repository writes, UI refreshes, and failure handling happen in a predictable order.
  @override
  Future<RepoResult<int>> deleteExpired({
    required core.OwnerId ownerId,
    required DateTime nowUtc,
  }) async {
    final expiredIds = _records.values
        .where(
          (record) =>
              record.ownerId == ownerId &&
              record.retentionExpiresAt.isBefore(nowUtc),
        )
        .map((record) => record.id)
        .toList(growable: false);
    for (final id in expiredIds) {
      _records.remove(id);
    }
    return Right(expiredIds.length);
  }
}

/// Top-level helper that performs the `_checkWrite<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.
Either<RepositoryFailure, _Record<T>> _checkWrite<T>({
  required Map<String, _Record<T>> records,
  required String id,
  required core.OwnerId ownerId,
  required core.Revision expectedRevision,
}) {
  if (expectedRevision.value <= 0) {
    core.logger.warn(() =>
        'In-memory repository write rejected invalid revision. '
        'entityId=$id ownerId=${ownerId.value} expectedRevision=${expectedRevision.value}');
    return Left(
      RepositoryInvalidRevision(
        entityId: id,
        expectedRevision: expectedRevision,
      ),
    );
  }
  final record = records[id];
  if (record == null) {
    core.logger.warn(() => 'In-memory repository write target missing. '
        'entityId=$id ownerId=${ownerId.value} expectedRevision=${expectedRevision.value}');
    return Left(RepositoryNotFound(entityId: id));
  }
  if (record.ownerId != ownerId) {
    core.logger.warn(() =>
        'In-memory repository write rejected owner mismatch. '
        'entityId=$id ownerId=${ownerId.value} recordOwnerId=${record.ownerId.value}');
    return Left(RepositoryOwnerMismatch(entityId: id));
  }
  if (record.revision != expectedRevision) {
    core.logger.warn(() =>
        'In-memory repository write rejected stale revision. '
        'entityId=$id ownerId=${ownerId.value} expectedRevision=${expectedRevision.value} '
        'actualRevision=${record.revision.value}');
    return Left(
      RepositoryStaleRevision(
        entityId: id,
        expectedRevision: expectedRevision,
        actualRevision: record.revision,
      ),
    );
  }
  return Right(record);
}

/// Runs the `R>` top-level operation.
/// This function is part of the file-level API and gives callers a direct way to use the associated scheduler behavior.
core.Page<R> _pageRecords<T extends _IdentifiedRecord, R>(
  Iterable<T> records,
  core.PageRequest page,
  R Function(T record) clone,
) {
  final ordered = records.toList(growable: false)
    ..sort((left, right) => left.id.compareTo(right.id));
  final start = _cursorOffset(page.cursor);
  final end = (start + page.limit).clamp(0, ordered.length);
  final items = ordered.sublist(start, end).map(clone).toList(growable: false);
  final nextCursor = end < ordered.length ? end.toString() : null;
  return core.Page<R>(items: items, nextCursor: nextCursor);
}

/// Top-level helper that performs the `_cursorOffset` 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 _cursorOffset(String? cursor) {
  if (cursor == null) {
    return 0;
  }
  final parsed = int.tryParse(cursor);
  if (parsed == null || parsed < 0) {
    return 0;
  }
  return parsed;
}

/// Top-level helper that performs the `_windowsOverlap` 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 _windowsOverlap(core.SchedulingWindow left, core.SchedulingWindow right) {
  return left.start.isBefore(right.end) && right.start.isBefore(left.end);
}

/// Top-level helper that performs the `_cloneTask` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
core.Task _cloneTask(
  core.Task task,
  core.OwnerId ownerId,
  core.Revision revision,
) {
  return core.TaskDocumentMapping.fromDocument(_roundTrip(
    core.TaskDocumentMapping.toDocument(
      task,
      ownerId: ownerId.value,
      revision: revision.value,
    ),
  ));
}

/// Top-level helper that performs the `_cloneProject` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
core.ProjectProfile _cloneProject(_Record<core.ProjectProfile> record) {
  return _cloneProjectValue(
    record.value,
    ownerId: record.ownerId,
    revision: record.revision,
    createdAt: record.createdAt,
    updatedAt: record.updatedAt,
  );
}

/// Top-level helper that performs the `_cloneProjectValue` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
core.ProjectProfile _cloneProjectValue(
  core.ProjectProfile project, {
  required core.OwnerId ownerId,
  required core.Revision revision,
  required DateTime createdAt,
  required DateTime updatedAt,
}) {
  return core.ProjectDocumentMapping.fromDocument(_roundTrip(
    core.ProjectDocumentMapping.toDocument(
      project,
      ownerId: ownerId.value,
      revision: revision.value,
      createdAt: createdAt,
      updatedAt: updatedAt,
    ),
  ));
}

/// Top-level helper that performs the `_cloneLockedBlock` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
core.LockedBlock _cloneLockedBlock(
  core.LockedBlock block,
  core.OwnerId ownerId,
  core.Revision revision,
) {
  return core.LockedBlockDocumentMapping.fromDocument(_roundTrip(
    core.LockedBlockDocumentMapping.toDocument(
      block,
      ownerId: ownerId.value,
      revision: revision.value,
    ),
  ));
}

/// Top-level helper that performs the `_cloneLockedBlockOverride` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
core.LockedBlockOverride _cloneLockedBlockOverride(
  core.LockedBlockOverride override,
  core.OwnerId ownerId,
  core.Revision revision,
) {
  return core.LockedBlockOverrideDocumentMapping.fromDocument(_roundTrip(
    core.LockedBlockOverrideDocumentMapping.toDocument(
      override,
      ownerId: ownerId.value,
      revision: revision.value,
    ),
  ));
}

/// Top-level helper that performs the `_cloneSettings` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
core.OwnerSettings _cloneSettings(_Record<core.OwnerSettings> record) {
  return _cloneSettingsValue(
    record.value,
    revision: record.revision,
    createdAt: record.createdAt,
    updatedAt: record.updatedAt,
  );
}

/// Top-level helper that performs the `_cloneSettingsValue` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
core.OwnerSettings _cloneSettingsValue(
  core.OwnerSettings settings, {
  required core.Revision revision,
  required DateTime createdAt,
  required DateTime updatedAt,
}) {
  return core.OwnerSettingsDocumentMapping.fromDocument(_roundTrip(
    core.OwnerSettingsDocumentMapping.toDocument(
      settings,
      revision: revision.value,
      createdAt: createdAt,
      updatedAt: updatedAt,
    ),
  ));
}

/// Top-level helper that performs the `_cloneSnapshot` operation for this file.
/// Keeping the helper private makes the surrounding flow easier to read while preventing unrelated libraries from depending on implementation detail.
core.SchedulingStateSnapshot _cloneSnapshot(
  core.SchedulingStateSnapshot snapshot,
  core.OwnerId ownerId,
  core.Revision revision,
) {
  return core.SchedulingSnapshotDocumentMapping.fromDocument(_roundTrip(
    core.SchedulingSnapshotDocumentMapping.toDocument(
      snapshot,
      ownerId: ownerId.value,
      revision: revision.value,
      retentionExpiresAt: snapshot.capturedAt.add(_snapshotRetention),
    ),
  ));
}

/// Top-level helper that performs the `_roundTrip` 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?> _roundTrip(Map<String, Object?> document) {
  return Map<String, Object?>.from(
    jsonDecode(jsonEncode(document)) as Map<dynamic, dynamic>,
  );
}
