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

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

/// In-memory task repository with owner scoping and revision checks.
final class InMemoryTaskRepository implements TaskRepository {
  /// 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, _Record<core.Task>> _records =
      <String, _Record<core.Task>>{};

  /// 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.Task?>> findById({
    required core.OwnerId ownerId,
    required String taskId,
  }) async {
    final record = _records[taskId];
    if (record == null || record.ownerId != ownerId) {
      return const Right(null);
    }
    return Right(_cloneTask(record.value, record.ownerId, record.revision));
  }

  /// Runs the `findByOwner` 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.Task>>> findByOwner({
    required core.OwnerId ownerId,
    core.PageRequest page = const core.PageRequest(),
  }) async {
    return Right(_pageRecords(
      _records.values.where((record) => record.ownerId == ownerId),
      page,
      (record) => _cloneTask(record.value, record.ownerId, record.revision),
    ));
  }

  /// Runs the `findByParentTaskId` 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.Task>>> findByParentTaskId({
    required core.OwnerId ownerId,
    required String parentTaskId,
    core.PageRequest page = const core.PageRequest(),
  }) async {
    return Right(_pageRecords(
      _records.values.where(
        (record) =>
            record.ownerId == ownerId &&
            record.value.parentTaskId == parentTaskId,
      ),
      page,
      (record) => _cloneTask(record.value, record.ownerId, record.revision),
    ));
  }

  /// Runs the `findByProjectId` 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.Task>>> findByProjectId({
    required core.OwnerId ownerId,
    required String projectId,
    core.PageRequest page = const core.PageRequest(),
  }) async {
    return Right(_pageRecords(
      _records.values.where(
        (record) =>
            record.ownerId == ownerId && record.value.projectId == projectId,
      ),
      page,
      (record) => _cloneTask(record.value, record.ownerId, record.revision),
    ));
  }

  /// Runs the `findByStatus` 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.Task>>> findByStatus({
    required core.OwnerId ownerId,
    required core.TaskStatus status,
    core.PageRequest page = const core.PageRequest(),
  }) async {
    return Right(_pageRecords(
      _records.values.where(
        (record) => record.ownerId == ownerId && record.value.status == status,
      ),
      page,
      (record) => _cloneTask(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.Task task,
  }) async {
    if (_records.containsKey(task.id)) {
      return Left(RepositoryDuplicateId(entityId: task.id));
    }
    _records[task.id] = _Record(
      id: task.id,
      ownerId: ownerId,
      value: _cloneTask(task, ownerId, core.Revision.initial),
      revision: core.Revision.initial,
    );
    return const Right(core.Revision.initial);
  }

  /// Runs the `save` 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>> save({
    required core.OwnerId ownerId,
    required core.Task task,
    required core.Revision expectedRevision,
  }) async {
    final checked = _checkWrite(
      records: _records,
      id: task.id,
      ownerId: ownerId,
      expectedRevision: expectedRevision,
    );
    if (checked is Left<RepositoryFailure, _Record<core.Task>>) {
      return Left(checked.value);
    }
    final record = checked.right;
    final nextRevision = record.revision.next();
    _records[task.id] = record.copyWith(
      value: _cloneTask(task, ownerId, nextRevision),
      revision: nextRevision,
      updatedAt: task.updatedAt,
    );
    return Right(nextRevision);
  }

  /// Runs the `delete` 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>> delete({
    required core.OwnerId ownerId,
    required String taskId,
    required core.Revision expectedRevision,
  }) async {
    final checked = _checkWrite(
      records: _records,
      id: taskId,
      ownerId: ownerId,
      expectedRevision: expectedRevision,
    );
    if (checked is Left<RepositoryFailure, _Record<core.Task>>) {
      return Left(checked.value);
    }
    final nextRevision = checked.right.revision.next();
    _records.remove(taskId);
    return Right(nextRevision);
  }
}
