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

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

/// In-memory locked-block repository with owner scoping and revision checks.
final class InMemoryLockedBlockRepository implements LockedBlockRepository {
  /// Private state stored as `_blocks` 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.LockedBlock>> _blocks =
      <String, _Record<core.LockedBlock>>{};

  /// Private state stored as `_overrides` 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.LockedBlockOverride>> _overrides =
      <String, _Record<core.LockedBlockOverride>>{};

  /// Runs the `findBlockById` 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.LockedBlock?>> findBlockById({
    required core.OwnerId ownerId,
    required String blockId,
  }) async {
    final record = _blocks[blockId];
    if (record == null || record.ownerId != ownerId) {
      return const Right(null);
    }
    return Right(
        _cloneLockedBlock(record.value, record.ownerId, record.revision));
  }

  /// Runs the `findBlocksByOwner` 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.LockedBlock>>> findBlocksByOwner({
    required core.OwnerId ownerId,
    bool includeArchived = false,
    core.PageRequest page = const core.PageRequest(),
  }) async {
    return Right(_pageRecords(
      _blocks.values.where(
        (record) =>
            record.ownerId == ownerId &&
            (includeArchived || !record.value.isArchived),
      ),
      page,
      (record) => _cloneLockedBlock(
        record.value,
        record.ownerId,
        record.revision,
      ),
    ));
  }

  /// Runs the `findOverrideById` 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.LockedBlockOverride?>> findOverrideById({
    required core.OwnerId ownerId,
    required String overrideId,
  }) async {
    final record = _overrides[overrideId];
    if (record == null || record.ownerId != ownerId) {
      return const Right(null);
    }
    return Right(
      _cloneLockedBlockOverride(record.value, record.ownerId, record.revision),
    );
  }

  /// Runs the `findOverridesForDate` 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.LockedBlockOverride>>> findOverridesForDate({
    required core.OwnerId ownerId,
    required core.CivilDate date,
    core.PageRequest page = const core.PageRequest(),
  }) async {
    return Right(_pageRecords(
      _overrides.values.where(
        (record) => record.ownerId == ownerId && record.value.date == date,
      ),
      page,
      (record) => _cloneLockedBlockOverride(
        record.value,
        record.ownerId,
        record.revision,
      ),
    ));
  }

  /// Runs the `insertBlock` 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>> insertBlock({
    required core.OwnerId ownerId,
    required core.LockedBlock block,
  }) async {
    if (_blocks.containsKey(block.id)) {
      return Left(RepositoryDuplicateId(entityId: block.id));
    }
    _blocks[block.id] = _Record(
      id: block.id,
      ownerId: ownerId,
      value: _cloneLockedBlock(block, ownerId, core.Revision.initial),
      revision: core.Revision.initial,
    );
    return const Right(core.Revision.initial);
  }

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

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

  /// Runs the `insertOverride` 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>> insertOverride({
    required core.OwnerId ownerId,
    required core.LockedBlockOverride override,
  }) async {
    if (_overrides.containsKey(override.id)) {
      return Left(RepositoryDuplicateId(entityId: override.id));
    }
    _overrides[override.id] = _Record(
      id: override.id,
      ownerId: ownerId,
      value: _cloneLockedBlockOverride(
        override,
        ownerId,
        core.Revision.initial,
      ),
      revision: core.Revision.initial,
    );
    return const Right(core.Revision.initial);
  }

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