// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

import 'dart:convert';

import 'package:http/http.dart';
import 'package:wyatt_http_client/src/models/middleware_context.dart';
import 'package:wyatt_http_client/src/models/middleware_request.dart';
import 'package:wyatt_http_client/src/models/middleware_response.dart';
import 'package:wyatt_http_client/src/models/unfreezed_request.dart';
import 'package:wyatt_http_client/src/pipeline.dart';
import 'package:wyatt_http_client/src/utils/http_methods.dart';

class MiddlewareClient extends BaseClient {

  MiddlewareClient({
    Pipeline? pipeline,
    Client? inner,
  })  : pipeline = pipeline ?? Pipeline(),
        inner = inner ?? Client() {
    print('Using Pipeline:\n$pipeline');
  }
  final Client inner;
  final Pipeline pipeline;

  @override
  Future<Response> head(Uri url, {Map<String, String>? headers}) =>
      _sendUnstreamed(HttpMethods.head.method, url, headers);

  @override
  Future<Response> get(Uri url, {Map<String, String>? headers}) =>
      _sendUnstreamed(HttpMethods.get.method, url, headers);

  @override
  Future<Response> post(
    Uri url, {
    Map<String, String>? headers,
    Object? body,
    Encoding? encoding,
  }) =>
      _sendUnstreamed(HttpMethods.post.method, url, headers, body, encoding);

  @override
  Future<Response> put(
    Uri url, {
    Map<String, String>? headers,
    Object? body,
    Encoding? encoding,
  }) =>
      _sendUnstreamed(HttpMethods.put.method, url, headers, body, encoding);

  @override
  Future<Response> patch(
    Uri url, {
    Map<String, String>? headers,
    Object? body,
    Encoding? encoding,
  }) =>
      _sendUnstreamed(HttpMethods.patch.method, url, headers, body, encoding);

  @override
  Future<Response> delete(
    Uri url, {
    Map<String, String>? headers,
    Object? body,
    Encoding? encoding,
  }) =>
      _sendUnstreamed(HttpMethods.delete.method, url, headers, body, encoding);

  @override
  Future<StreamedResponse> send(BaseRequest request) => inner.send(request);

  Future<Response> _sendUnstreamed(
    String method,
    Uri url,
    Map<String, String>? headers, [
    Object? body,
    Encoding? encoding,
  ]) async {
    final originalRequest = MiddlewareRequest(
      unfreezedRequest: UnfreezedRequest(
        method: method,
        url: url,
        headers: headers,
        body: body,
        encoding: encoding,
      ),
    );
    final requestContext = MiddlewareContext(
      pipeline: pipeline,
      client: this,
      originalRequest: originalRequest,
    );

    final modifiedRequest = await pipeline.onRequest(
      requestContext,
      originalRequest.copyWith(),
    );

    final originalResponse = MiddlewareResponse(
      httpResponse: await Response.fromStream(
        await send(modifiedRequest.request),
      ),
    );

    final responseContext =
        requestContext.copyWith(originalResponse: originalResponse);

    final modifiedResponse =
        await pipeline.onResponse(responseContext, originalResponse.copyWith());

    return modifiedResponse.httpResponse as Response;
  }
}
