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

part of '../../../../application_layer.dart';

/// Private implementation type for `_UnitOfWorkLockedBlockRepository` in this library.
/// It keeps supporting state and behavior scoped to the owning file so the public API stays focused on the scheduler concepts that callers use directly.
class _UnitOfWorkLockedBlockRepository implements LockedBlockRepository {
  /// Creates a `_UnitOfWorkLockedBlockRepository` instance with the values required by its invariants.
  /// Callers should pass already-normalized scheduler data because validation and defaulting belong at the model or service boundary, not in arbitrary UI code.
  _UnitOfWorkLockedBlockRepository({
    required Map<String, LockedBlock> blocksById,
    required Map<String, String> blockOwnersById,
    required Map<String, int> blockRevisionsById,
    required Map<String, LockedBlockOverride> overridesById,
    required Map<String, String> overrideOwnersById,
    required Map<String, int> overrideRevisionsById,
  })  : _blocksById = blocksById,
        _blockOwnersById = blockOwnersById,
        _blockRevisionsById = blockRevisionsById,
        _overridesById = overridesById,
        _overrideOwnersById = overrideOwnersById,
        _overrideRevisionsById = overrideRevisionsById;

  /// Private state stored as `_blocksById` 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, LockedBlock> _blocksById;

  /// Private state stored as `_blockOwnersById` 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, String> _blockOwnersById;

  /// Private state stored as `_blockRevisionsById` 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, int> _blockRevisionsById;

  /// Private state stored as `_overridesById` 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, LockedBlockOverride> _overridesById;

  /// Private state stored as `_overrideOwnersById` 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, String> _overrideOwnersById;

  /// Private state stored as `_overrideRevisionsById` 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, int> _overrideRevisionsById;

  /// 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<LockedBlock?> findBlockById(String id) async => _blocksById[id];

  /// Runs the `findBlockRecordById` 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<RepositoryRecord<LockedBlock>?> findBlockRecordById(String id) async {
    final block = _blocksById[id];
    if (block == null) {
      return null;
    }
    return RepositoryRecord(
      value: block,
      ownerId: _blockOwnerFor(id),
      revision: _blockRevisionFor(id),
      archivedAt: block.archivedAt,
    );
  }

  /// Runs the `findAllBlocks` 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<List<LockedBlock>> findAllBlocks() async {
    return List<LockedBlock>.unmodifiable(_blocksById.values);
  }

  /// 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<RepositoryPage<LockedBlock>> findBlocksByOwner({
    required String ownerId,
    bool includeArchived = false,
    RepositoryPageRequest page = const RepositoryPageRequest(),
  }) async {
    final blocks = _blocksById.values
        .where(
          (block) =>
              _blockOwnerFor(block.id) == ownerId &&
              (includeArchived || !block.isArchived),
        )
        .toList(growable: false)
      ..sort(_compareLockedBlocks);
    return _page(blocks, page);
  }

  /// 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<LockedBlockOverride?> findOverrideById(String id) async {
    return _overridesById[id];
  }

  /// Runs the `findOverrideRecordById` 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<RepositoryRecord<LockedBlockOverride>?> findOverrideRecordById(
    String id,
  ) async {
    final override = _overridesById[id];
    if (override == null) {
      return null;
    }
    return RepositoryRecord(
      value: override,
      ownerId: _overrideOwnerFor(id),
      revision: _overrideRevisionFor(id),
    );
  }

  /// Runs the `findAllOverrides` 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<List<LockedBlockOverride>> findAllOverrides() async {
    return List<LockedBlockOverride>.unmodifiable(_overridesById.values);
  }

  /// 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<RepositoryPage<LockedBlockOverride>> findOverridesForDate({
    required String ownerId,
    required CivilDate date,
    RepositoryPageRequest page = const RepositoryPageRequest(),
  }) async {
    final overrides = _overridesById.values
        .where(
          (override) =>
              _overrideOwnerFor(override.id) == ownerId &&
              override.date == date,
        )
        .toList(growable: false)
      ..sort(_compareLockedOverrides);
    return _page(overrides, page);
  }

  /// 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<void> saveBlock(LockedBlock block) async {
    _blocksById[block.id] = block;
    _blockOwnersById.putIfAbsent(block.id, () => 'owner-1');
    _blockRevisionsById[block.id] = _blockRevisionFor(block.id) + 1;
  }

  /// 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<void> saveOverride(LockedBlockOverride override) async {
    _overridesById[override.id] = override;
    _overrideOwnersById.putIfAbsent(override.id, () => 'owner-1');
    _overrideRevisionsById[override.id] = _overrideRevisionFor(override.id) + 1;
  }

  /// 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<RepositoryMutationResult> insertBlock({
    required LockedBlock block,
    required String ownerId,
  }) async {
    if (_blocksById.containsKey(block.id)) {
      return RepositoryMutationResult.failure(
        RepositoryFailure(
          code: RepositoryFailureCode.duplicateId,
          entityId: block.id,
        ),
      );
    }
    _blocksById[block.id] = block;
    _blockOwnersById[block.id] = ownerId;
    _blockRevisionsById[block.id] = 1;
    return RepositoryMutationResult.success(revision: 1);
  }

  /// Runs the `saveBlockIfRevision` 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<RepositoryMutationResult> saveBlockIfRevision({
    required LockedBlock block,
    required String ownerId,
    required int expectedRevision,
  }) async {
    if (!_blocksById.containsKey(block.id) ||
        _blockOwnerFor(block.id) != ownerId) {
      return RepositoryMutationResult.failure(
        RepositoryFailure(
          code: RepositoryFailureCode.notFound,
          entityId: block.id,
        ),
      );
    }
    final actualRevision = _blockRevisionFor(block.id);
    if (actualRevision != expectedRevision) {
      return RepositoryMutationResult.failure(
        RepositoryFailure(
          code: RepositoryFailureCode.staleRevision,
          entityId: block.id,
          expectedRevision: expectedRevision,
          actualRevision: actualRevision,
        ),
      );
    }
    final nextRevision = actualRevision + 1;
    _blocksById[block.id] = block;
    _blockOwnersById[block.id] = ownerId;
    _blockRevisionsById[block.id] = nextRevision;
    return RepositoryMutationResult.success(revision: 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<RepositoryMutationResult> archiveBlock({
    required String blockId,
    required String ownerId,
    required int expectedRevision,
    required DateTime archivedAt,
  }) async {
    final block = _blocksById[blockId];
    if (block == null) {
      return RepositoryMutationResult.failure(
        RepositoryFailure(
          code: RepositoryFailureCode.notFound,
          entityId: blockId,
        ),
      );
    }
    return saveBlockIfRevision(
      block: block.copyWith(updatedAt: archivedAt, archivedAt: archivedAt),
      ownerId: ownerId,
      expectedRevision: expectedRevision,
    );
  }

  /// 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<RepositoryMutationResult> insertOverride({
    required LockedBlockOverride override,
    required String ownerId,
  }) async {
    if (_overridesById.containsKey(override.id)) {
      return RepositoryMutationResult.failure(
        RepositoryFailure(
          code: RepositoryFailureCode.duplicateId,
          entityId: override.id,
        ),
      );
    }
    _overridesById[override.id] = override;
    _overrideOwnersById[override.id] = ownerId;
    _overrideRevisionsById[override.id] = 1;
    return RepositoryMutationResult.success(revision: 1);
  }

  /// Runs the `saveOverrideIfRevision` 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<RepositoryMutationResult> saveOverrideIfRevision({
    required LockedBlockOverride override,
    required String ownerId,
    required int expectedRevision,
  }) async {
    if (!_overridesById.containsKey(override.id) ||
        _overrideOwnerFor(override.id) != ownerId) {
      return RepositoryMutationResult.failure(
        RepositoryFailure(
          code: RepositoryFailureCode.notFound,
          entityId: override.id,
        ),
      );
    }
    final actualRevision = _overrideRevisionFor(override.id);
    if (actualRevision != expectedRevision) {
      return RepositoryMutationResult.failure(
        RepositoryFailure(
          code: RepositoryFailureCode.staleRevision,
          entityId: override.id,
          expectedRevision: expectedRevision,
          actualRevision: actualRevision,
        ),
      );
    }
    final nextRevision = actualRevision + 1;
    _overridesById[override.id] = override;
    _overrideOwnersById[override.id] = ownerId;
    _overrideRevisionsById[override.id] = nextRevision;
    return RepositoryMutationResult.success(revision: nextRevision);
  }

  /// Runs the `_blockOwnerFor` helper used inside this library.
  /// The helper centralizes a small piece of transformation or validation so higher-level scheduler flow remains easier to read.
  String _blockOwnerFor(String id) => _blockOwnersById[id] ?? 'owner-1';

  /// Runs the `_blockRevisionFor` helper used inside this library.
  /// The helper centralizes a small piece of transformation or validation so higher-level scheduler flow remains easier to read.
  int _blockRevisionFor(String id) => _blockRevisionsById[id] ?? 1;

  /// Runs the `_overrideOwnerFor` helper used inside this library.
  /// The helper centralizes a small piece of transformation or validation so higher-level scheduler flow remains easier to read.
  String _overrideOwnerFor(String id) => _overrideOwnersById[id] ?? 'owner-1';

  /// Runs the `_overrideRevisionFor` helper used inside this library.
  /// The helper centralizes a small piece of transformation or validation so higher-level scheduler flow remains easier to read.
  int _overrideRevisionFor(String id) => _overrideRevisionsById[id] ?? 1;
}
