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

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

/// In-memory project repository with owner scoping and revision checks.
final class InMemoryProjectRepository implements ProjectRepository {
  /// 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.ProjectProfile>> _records =
      <String, _Record<core.ProjectProfile>>{};

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

  /// 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.ProjectProfile>>> findByOwner({
    required core.OwnerId ownerId,
    bool includeArchived = false,
    core.PageRequest page = const core.PageRequest(),
  }) async {
    return Right(_pageRecords(
      _records.values.where(
        (record) =>
            record.ownerId == ownerId &&
            (includeArchived || !record.value.isArchived),
      ),
      page,
      _cloneProject,
    ));
  }

  /// 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.ProjectProfile project,
  }) async {
    if (_records.containsKey(project.id)) {
      return Left(RepositoryDuplicateId(entityId: project.id));
    }
    _records[project.id] = _Record(
      id: project.id,
      ownerId: ownerId,
      value: _cloneProjectValue(
        project,
        ownerId: ownerId,
        revision: core.Revision.initial,
        createdAt: _epoch,
        updatedAt: _epoch,
      ),
      revision: core.Revision.initial,
      createdAt: _epoch,
      updatedAt: _epoch,
    );
    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.ProjectProfile project,
    required core.Revision expectedRevision,
  }) async {
    final checked = _checkWrite(
      records: _records,
      id: project.id,
      ownerId: ownerId,
      expectedRevision: expectedRevision,
    );
    if (checked is Left<RepositoryFailure, _Record<core.ProjectProfile>>) {
      return Left(checked.value);
    }
    final record = checked.right;
    final nextRevision = record.revision.next();
    _records[project.id] = record.copyWith(
      value: _cloneProjectValue(
        project,
        ownerId: ownerId,
        revision: nextRevision,
        createdAt: record.createdAt,
        updatedAt: _epoch,
      ),
      revision: nextRevision,
      updatedAt: _epoch,
    );
    return Right(nextRevision);
  }

  /// Runs the `archive` 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>> archive({
    required core.OwnerId ownerId,
    required String projectId,
    required core.Revision expectedRevision,
    required DateTime archivedAtUtc,
  }) async {
    final checked = _checkWrite(
      records: _records,
      id: projectId,
      ownerId: ownerId,
      expectedRevision: expectedRevision,
    );
    if (checked is Left<RepositoryFailure, _Record<core.ProjectProfile>>) {
      return Left(checked.value);
    }
    final record = checked.right;
    final nextRevision = record.revision.next();
    final archived = record.value.copyWith(archivedAt: archivedAtUtc);
    _records[projectId] = record.copyWith(
      value: _cloneProjectValue(
        archived,
        ownerId: ownerId,
        revision: nextRevision,
        createdAt: record.createdAt,
        updatedAt: _epoch,
      ),
      revision: nextRevision,
      updatedAt: _epoch,
    );
    return Right(nextRevision);
  }
}
