// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:async';

import 'package:async/async.dart';

import 'handler.dart';
import 'request.dart';
import 'response.dart';
import 'server.dart';

/// A connected pair of a [Server] and a [Handler].
///
/// Requests to the handler are sent to the server's mounted handler once it's
/// available. This is used to expose a virtual [Server] that's actually one
/// part of a larger URL-space.
@Deprecated('Do not use. If you have a use case for this class add a comment '
    'at https://github.com/dart-lang/shelf/issues/205')
class ServerHandler {
  /// The server.
  ///
  /// Once this has a handler mounted, it's passed all requests to [handler]
  /// until this server is closed.
  Server get server => _server;
  final _HandlerServer _server;

  /// The handler.
  ///
  /// This passes requests to [server]'s handler. If that handler isn't mounted
  /// yet, the requests are handled once it is.
  Handler get handler => _onRequest;

  /// Creates a new connected pair of a [Server] with the given [url] and a
  /// [Handler].
  ///
  /// The caller is responsible for ensuring that requests to [url] or any URL
  /// beneath it are handled by [handler].
  ///
  /// If [onClose] is passed, it's called when [server] is closed. It may return
  /// a [Future] or `null`; its return value is returned by [Server.close].
  ServerHandler(Uri url, {Future<void>? Function()? onClose})
      : _server = _HandlerServer(url, onClose);

  /// Pipes requests to [server]'s handler.
  FutureOr<Response> _onRequest(Request request) {
    if (_server._closeMemo.hasRun) {
      throw StateError('Request received after the server was closed.');
    }

    if (_server._handler != null) return _server._handler!(request);

    // Avoid async/await so that the common case of a handler already being
    // mounted doesn't involve any extra asynchronous delays.
    return _server._onMounted.then((_) => _server._handler!(request));
  }
}

// ignore: deprecated_member_use_from_same_package
/// The [Server] returned by [ServerHandler].
class _HandlerServer implements Server {
  @override
  final Uri url;

  /// The callback to call when [close] is called, or `null`.
  final ZoneCallback<void>? _onClose;

  /// The mounted handler.
  ///
  /// This is `null` until [mount] is called.
  Handler? _handler;

  /// A future that fires once [mount] has been called.
  Future<void> get _onMounted => _onMountedCompleter.future;
  final _onMountedCompleter = Completer<void>();

  _HandlerServer(this.url, this._onClose);

  @override
  void mount(Handler handler) {
    if (_handler != null) {
      throw StateError("Can't mount two handlers for the same server.");
    }

    _handler = handler;
    _onMountedCompleter.complete();
  }

  @override
  Future<void> close() => _closeMemo.runOnce(() {
        return _onClose == null ? null : _onClose();
      });
  final _closeMemo = AsyncMemoizer<void>();
}
