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

part of '../sqlite_repositories.dart';

/// Drift-backed owner settings repository.
final class SqliteSettingsRepository implements SettingsRepository {
  /// Creates a settings repository using [db].
  SqliteSettingsRepository(this.db);

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

  /// 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.OwnerSettings?>> findByOwner({
    required core.OwnerId ownerId,
  }) async {
    final row = await _settingsByOwner(ownerId.value);
    if (row == null) return const Right(null);
    return Right(_settingsFromRow(row));
  }

  /// 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.OwnerSettings settings,
  }) async {
    if (await _settingsByOwner(ownerId.value) != null) {
      return Left(RepositoryDuplicateId(entityId: ownerId.value));
    }
    final ownedSettings = settings.ownerId == ownerId.value
        ? settings
        : settings.copyWith(ownerId: ownerId.value);
    await db.into(db.settingsTable).insert(_settingsCompanion(
          ownedSettings,
          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.OwnerSettings settings,
    required core.Revision expectedRevision,
  }) async {
    final checked = await _checkSettingsWrite(ownerId, expectedRevision);
    if (checked is Left<RepositoryFailure, SettingsRow>) {
      return Left(checked.value);
    }
    final row = checked.right;
    final nextRevision = core.Revision(row.revision).next();
    final ownedSettings = settings.ownerId == ownerId.value
        ? settings
        : settings.copyWith(ownerId: ownerId.value);
    final updated = await (db.update(db.settingsTable)
          ..where(
            (table) =>
                table.ownerId.equals(ownerId.value) &
                table.revision.equals(expectedRevision.value),
          ))
        .write(_settingsCompanion(
      ownedSettings,
      revision: nextRevision,
      createdAt: row.createdAtUtc,
      updatedAt: _epoch,
    ));
    if (updated != 1) {
      return Left(await _staleSettingsFailure(ownerId, expectedRevision));
    }
    return Right(nextRevision);
  }

  /// Runs the `_settingsByOwner` 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.
  Future<SettingsRow?> _settingsByOwner(String ownerId) {
    return (db.select(db.settingsTable)
          ..where((table) => table.ownerId.equals(ownerId)))
        .getSingleOrNull();
  }

  /// Runs the `_checkSettingsWrite` 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.
  Future<Either<RepositoryFailure, SettingsRow>> _checkSettingsWrite(
    core.OwnerId ownerId,
    core.Revision expectedRevision,
  ) async {
    if (expectedRevision.value <= 0) {
      return Left(RepositoryInvalidRevision(
        entityId: ownerId.value,
        expectedRevision: expectedRevision,
      ));
    }
    final row = await _settingsByOwner(ownerId.value);
    if (row == null) return Left(RepositoryNotFound(entityId: ownerId.value));
    if (row.revision != expectedRevision.value) {
      return Left(RepositoryStaleRevision(
        entityId: ownerId.value,
        expectedRevision: expectedRevision,
        actualRevision: core.Revision(row.revision),
      ));
    }
    return Right(row);
  }

  /// Runs the `_staleSettingsFailure` 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.
  Future<RepositoryStaleRevision> _staleSettingsFailure(
    core.OwnerId ownerId,
    core.Revision expectedRevision,
  ) async {
    final row = await _settingsByOwner(ownerId.value);
    return RepositoryStaleRevision(
      entityId: ownerId.value,
      expectedRevision: expectedRevision,
      actualRevision:
          row == null ? expectedRevision.next() : core.Revision(row.revision),
    );
  }
}
