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

part of '../sqlite_repositories.dart';

/// SQLite implementation of the app-layer notice acknowledgement repository.
final class _SqliteApplicationNoticeAcknowledgementRepository
    implements core.NoticeAcknowledgementRepository {
  /// Creates a notice acknowledgement repository for [db].
  _SqliteApplicationNoticeAcknowledgementRepository(this.db);

  /// Shared SQLite database handle.
  final SchedulerDb db;

  /// Returns a notice acknowledgement by owner and notice id.
  @override
  Future<core.NoticeAcknowledgementRecord?> findById({
    required String ownerId,
    required String noticeId,
  }) async {
    final row = await _noticeById(ownerId: ownerId, noticeId: noticeId);
    return row == null ? null : _noticeFromRow(row);
  }

  /// Returns acknowledgements for [ownerId].
  @override
  Future<List<core.NoticeAcknowledgementRecord>> findByOwnerId(
    String ownerId,
  ) async {
    final rows = await (db.select(db.noticeAcknowledgements)
          ..where((table) => table.ownerId.equals(ownerId))
          ..orderBy([
            (table) => drift.OrderingTerm.asc(table.acknowledgedAtUtc),
            (table) => drift.OrderingTerm.asc(table.noticeId),
          ]))
        .get();
    return List<core.NoticeAcknowledgementRecord>.unmodifiable(
      rows.map(_noticeFromRow),
    );
  }

  /// Returns owner-scoped acknowledgements.
  @override
  Future<core.RepositoryPage<core.NoticeAcknowledgementRecord>> findByOwner({
    required String ownerId,
    core.RepositoryPageRequest page = const core.RepositoryPageRequest(),
  }) async {
    final rows = await (db.select(db.noticeAcknowledgements)
          ..where((table) => table.ownerId.equals(ownerId))
          ..orderBy([
            (table) => drift.OrderingTerm.asc(table.acknowledgedAtUtc),
            (table) => drift.OrderingTerm.asc(table.noticeId),
          ])
          ..limit(
            page.limit + 1,
            offset: _applicationCursorOffset(page.cursor),
          ))
        .get();
    return _applicationPageFromRows(rows, page, _noticeFromRow);
  }

  /// Saves [record], inserting it when absent.
  @override
  Future<void> save(core.NoticeAcknowledgementRecord record) async {
    final row = await _noticeById(
      ownerId: record.ownerId,
      noticeId: record.noticeId,
    );
    if (row == null) {
      await db.into(db.noticeAcknowledgements).insert(
            _noticeCompanion(
              record,
              revision: 1,
              createdAt: record.acknowledgedAt,
              updatedAt: record.acknowledgedAt,
            ),
          );
      return;
    }
    await (db.update(db.noticeAcknowledgements)
          ..where(
            (table) =>
                table.ownerId.equals(record.ownerId) &
                table.noticeId.equals(record.noticeId),
          ))
        .write(
      _noticeCompanion(
        record,
        revision: row.revision + 1,
        createdAt: row.createdAtUtc,
        updatedAt: record.acknowledgedAt,
      ),
    );
  }

  /// Inserts [record] when absent.
  @override
  Future<core.RepositoryMutationResult> insert(
    core.NoticeAcknowledgementRecord record,
  ) async {
    if (await _noticeById(
          ownerId: record.ownerId,
          noticeId: record.noticeId,
        ) !=
        null) {
      return _mutationFailure(
        core.RepositoryFailureCode.duplicateId,
        entityId: record.noticeId,
      );
    }
    await db.into(db.noticeAcknowledgements).insert(
          _noticeCompanion(
            record,
            revision: 1,
            createdAt: record.acknowledgedAt,
            updatedAt: record.acknowledgedAt,
          ),
        );
    return core.RepositoryMutationResult.success(revision: 1);
  }

  /// Returns a raw notice row by owner and notice id.
  Future<NoticeAcknowledgementRow?> _noticeById({
    required String ownerId,
    required String noticeId,
  }) {
    return (db.select(db.noticeAcknowledgements)
          ..where(
            (table) =>
                table.ownerId.equals(ownerId) & table.noticeId.equals(noticeId),
          ))
        .getSingleOrNull();
  }
}

/// Builds a Drift companion for [record].
NoticeAcknowledgementsCompanion _noticeCompanion(
  core.NoticeAcknowledgementRecord record, {
  required int revision,
  required DateTime createdAt,
  required DateTime updatedAt,
}) {
  return NoticeAcknowledgementsCompanion.insert(
    ownerId: record.ownerId,
    noticeId: record.noticeId,
    acknowledgedAtUtc: record.acknowledgedAt.toUtc(),
    revision: revision,
    createdAtUtc: createdAt.toUtc(),
    updatedAtUtc: updatedAt.toUtc(),
  );
}

/// Converts a notice acknowledgement row to a core record.
core.NoticeAcknowledgementRecord _noticeFromRow(NoticeAcknowledgementRow row) {
  return core.NoticeAcknowledgementRecord(
    noticeId: row.noticeId,
    ownerId: row.ownerId,
    acknowledgedAt: row.acknowledgedAtUtc.toUtc(),
  );
}
