import 'dart:async';
import 'package:pool/pool.dart';
import 'package:postgres/postgres.dart';

/// Connects to a PostgreSQL database, whether synchronously or asynchronously.
typedef FutureOr<PostgreSQLConnection> PostgreSQLConnector();

/// Pools connections to a PostgreSQL database.
class PostgreSQLConnectionPool {
  Pool _pool;

  /// The maximum number of concurrent connections to the database.
  ///
  /// Default: `5`
  final int concurrency;

  /// An optional timeout for pooled connections to execute.
  final Duration timeout;

  /// A function that connects this pool to the database, on-demand.
  final PostgreSQLConnector connector;

  PostgreSQLConnectionPool(this.connector,
      {this.concurrency: 5, this.timeout}) {
    _pool = new Pool(concurrency, timeout: timeout);
  }

  Future<PostgreSQLConnection> _connect() async {
    var connection = await connector() as PostgreSQLConnection;
    await connection.open();
    return connection;
  }

  /// Connects to the database, and then executes the [callback].
  ///
  /// Returns the result of [callback].
  Future<T> run<T>(FutureOr<T> callback(PostgreSQLConnection connection)) {
    return _pool.request().then((resx) {
      return _connect().then((connection) {
        return new Future<T>.sync(() => callback(connection))
            .whenComplete(() async {
          if (!connection.isClosed) await connection.close();
          resx.release();
        });
      });
    });
  }
}