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

part of '../sqlite_repositories.dart';

/// SQLite-backed application transaction boundary.
final class SqliteApplicationUnitOfWork implements core.ApplicationUnitOfWork {
  /// Creates an application unit of work backed by [db].
  SqliteApplicationUnitOfWork(
    this.db, {
    this.defaultOwnerId = SqliteApplicationRuntime.defaultV1OwnerId,
  });

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

  /// Owner used by app-layer save methods that do not carry owner explicitly.
  final String defaultOwnerId;

  /// Runs an application command atomically in a Drift transaction.
  @override
  Future<core.ApplicationResult<T>> run<T>({
    required core.ApplicationOperationContext context,
    required String operationName,
    required Future<core.ApplicationResult<T>> Function(
      core.ApplicationUnitOfWorkRepositories repositories,
    ) action,
  }) {
    core.logger.debug(() => 'SQLite unit of work run started. '
        'operationId=${context.operationId} operationName=$operationName '
        'ownerId=${context.ownerTimeZone.ownerId}');
    return _guardApplicationErrors(() {
      return db.transaction(() async {
        final repositories = _SqliteApplicationRepositories(
          db,
          defaultOwnerId: context.ownerTimeZone.ownerId,
        );
        final existing = await repositories.operations.findById(
          context.operationId,
        );
        if (existing != null) {
          core.logger.warn(() =>
              'Duplicate operation ignored by SQLite unit of work. '
              'operationId=${context.operationId} operationName=$operationName');
          return core.ApplicationResult.failure(
            core.ApplicationFailure(
              code: core.ApplicationFailureCode.duplicateOperation,
              entityId: context.operationId,
            ),
          );
        }

        final result = await action(repositories);
        if (result.isFailure) {
          core.logger.warn(() => 'SQLite unit of work action returned failure. '
              'operationId=${context.operationId} operationName=$operationName '
              'code=${result.failure!.code.name} '
              'detailCode=${result.failure!.detailCode}');
          return result;
        }

        final operationResult = await repositories.operations.insertIfAbsent(
          core.ApplicationOperationRecord(
            operationId: context.operationId,
            ownerId: context.ownerTimeZone.ownerId,
            operationName: operationName,
            committedAt: context.now,
          ),
        );
        if (!operationResult.isSuccess) {
          core.logger.warn(() =>
              'SQLite unit of work operation record insert conflicted. '
              'operationId=${context.operationId} operationName=$operationName');
          return core.ApplicationResult.failure(
            core.ApplicationFailure(
              code: core.ApplicationFailureCode.duplicateOperation,
              entityId: context.operationId,
            ),
          );
        }

        core.logger.debug(() => 'SQLite unit of work run committed. '
            'operationId=${context.operationId} operationName=$operationName');
        return result;
      });
    });
  }

  /// Runs an application read against SQLite repositories.
  @override
  Future<core.ApplicationResult<T>> read<T>({
    required Future<core.ApplicationResult<T>> Function(
      core.ApplicationUnitOfWorkRepositories repositories,
    ) action,
  }) {
    core.logger.debug(() => 'SQLite unit of work read started. '
        'defaultOwnerId=$defaultOwnerId');
    return _guardApplicationErrors(() async {
      final result = await action(
        _SqliteApplicationRepositories(
          db,
          defaultOwnerId: defaultOwnerId,
        ),
      );
      if (result.isFailure) {
        core.logger.warn(() => 'SQLite unit of work read returned failure. '
            'code=${result.failure!.code.name} detailCode=${result.failure!.detailCode}');
      } else {
        core.logger.debug(() => 'SQLite unit of work read finished. '
            'defaultOwnerId=$defaultOwnerId');
      }
      return result;
    });
  }
}

/// Converts expected application exceptions into typed application failures.
Future<core.ApplicationResult<T>> _guardApplicationErrors<T>(
  Future<core.ApplicationResult<T>> Function() action,
) async {
  try {
    return await action();
  } on core.ApplicationFailureException catch (error) {
    core.logger.warn(() => 'SQLite application caught application failure. '
        'code=${error.failure.code.name} detailCode=${error.failure.detailCode}');
    return core.ApplicationResult.failure(error.failure);
  } on core.DomainValidationException catch (error) {
    core.logger
        .warn(() => 'SQLite application caught domain validation failure. '
            'code=${error.code.name}');
    return core.ApplicationResult.failure(
      core.ApplicationFailure(
        code: core.ApplicationFailureCode.validation,
        detailCode: error.code.name,
      ),
    );
  } on ArgumentError catch (error) {
    core.logger
        .warn(() => 'SQLite application caught argument validation failure. '
            'argumentName=${error.name}');
    return core.ApplicationResult.failure(
      core.ApplicationFailure(
        code: core.ApplicationFailureCode.validation,
        detailCode: error.name,
      ),
    );
  } on core.ApplicationPersistenceException catch (error, stackTrace) {
    core.logger.error(
      () => 'SQLite application persistence failure.',
      error: error,
      stackTrace: stackTrace,
    );
    return core.ApplicationResult.failure(
      const core.ApplicationFailure(
        code: core.ApplicationFailureCode.persistenceFailure,
      ),
    );
  } on sqlite.SqliteException catch (error, stackTrace) {
    core.logger.error(
      () => 'SQLite database operation failed. errorType=${error.runtimeType}',
      error: error,
      stackTrace: stackTrace,
    );
    return core.ApplicationResult.failure(
      const core.ApplicationFailure(
        code: core.ApplicationFailureCode.persistenceFailure,
      ),
    );
  } catch (error, stackTrace) {
    core.logger.error(
      () => 'SQLite application unexpected failure.',
      error: error,
      stackTrace: stackTrace,
    );
    return core.ApplicationResult.failure(
      const core.ApplicationFailure(
          code: core.ApplicationFailureCode.unexpected),
    );
  }
}
