import 'dart:async';

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:fcs/domain/constants.dart';
import 'package:fcs/domain/entities/cargo_type.dart';
import 'package:fcs/domain/entities/custom_duty.dart';
import 'package:fcs/domain/entities/discount_by_weight.dart';
import 'package:fcs/domain/entities/rate.dart';
import 'package:fcs/helpers/api_helper.dart';
import 'package:fcs/helpers/firebase_helper.dart';
import 'package:logging/logging.dart';

class RateDataProvider {
  final log = Logger('RateDataProvider');

  static final RateDataProvider instance = RateDataProvider._();
  RateDataProvider._();

  StreamController<Rate> controller;
  static Rate _rate = Rate();

  Stream<Rate> _rateStream() async* {
    Stream<DocumentSnapshot> snapshot = Firestore.instance
        .collection(config_collection)
        .document(rate_doc_id)
        .snapshots();
    await for (var snap in snapshot) {
      Rate rate = Rate.fromMap(snap.data);
      yield rate;
    }
  }

  Stream<List<CargoType>> _cargoTypeStream() async* {
    List<CargoType> cargoTypes = [];
    Stream<QuerySnapshot> snapshots = Firestore.instance
        .collection(config_collection)
        .document(rate_doc_id)
        .collection(cargo_types_collection)
        .snapshots();

    await for (var snaps in snapshots) {
      cargoTypes = [];
      cargoTypes = snaps.documents.map((snap) {
        return CargoType.fromMap(snap.data, snap.documentID);
      }).toList();

      yield cargoTypes;
    }
  }

  Stream<List<CustomDuty>> _customDutiesStream() async* {
    List<CustomDuty> customDuries = [];
    Stream<QuerySnapshot> snapshots = Firestore.instance
        .collection(config_collection)
        .document(rate_doc_id)
        .collection(custom_duties_collection)
        .snapshots();

    await for (var snaps in snapshots) {
      customDuries = [];
      customDuries = snaps.documents.map((snap) {
        return CustomDuty.fromMap(snap.data, snap.documentID);
      }).toList();
      yield customDuries;
    }
  }

  Stream<List<DiscountByWeight>> _discountByWeightStream() async* {
    List<DiscountByWeight> discountByWeight = [];
    Stream<QuerySnapshot> snapshots = Firestore.instance
        .collection(config_collection)
        .document(rate_doc_id)
        .collection(discounts_by_weights_collection)
        .snapshots();

    await for (var snaps in snapshots) {
      discountByWeight = [];
      discountByWeight = snaps.documents.map((snap) {
        return DiscountByWeight.fromMap(snap.data, snap.documentID);
      }).toList();
      yield discountByWeight;
    }
  }

  StreamSubscription<Rate> rateListener;
  StreamSubscription<List<CargoType>> cargoListener;
  StreamSubscription<List<CustomDuty>> customListener;
  StreamSubscription<List<DiscountByWeight>> discountListener;
  Stream<Rate> rate() {
    Future<void> _start() async {
      rateListener = _rateStream().listen((rate) {
        _rate.deliveryFee = rate.deliveryFee;
        _rate.freeDeliveryWeight = rate.freeDeliveryWeight;
        _rate.volumetricRatio = rate.volumetricRatio;
        controller.add(_rate);
      });
      cargoListener = _cargoTypeStream().listen((cargoTypes) {
        _rate.cargoTypes = cargoTypes;
        controller.add(_rate);
      });
      customListener = _customDutiesStream().listen((customDuties) {
        _rate.customDuties = customDuties;
        controller.add(_rate);
      });
      discountListener = _discountByWeightStream().listen((discountByWeights) {
        _rate.discountByWeights = discountByWeights;
        controller.add(_rate);
      });
    }

    void _stop() {
      if (rateListener != null) {
        rateListener.cancel();
      }
      if (cargoListener != null) {
        cargoListener.cancel();
      }
      if (customListener != null) {
        customListener.cancel();
      }
      if (discountListener != null) {
        discountListener.cancel();
      }
    }

    controller = StreamController<Rate>(
        onListen: _start, onPause: _stop, onResume: _start, onCancel: _stop);

    return controller.stream;
  }

  Future<void> updateRates(Rate rate) async {
    return await requestAPI("/rates", "PUT",
        payload: rate.toMap(), token: await getToken());
  }

  Future<void> createCargoType(CargoType cargoType) async {
    return await requestAPI("/cargo_types", "POST",
        payload: cargoType.toMap(), token: await getToken());
  }

  Future<void> updateCargoType(CargoType cargoType) async {
    return await requestAPI("/cargo_types", "PUT",
        payload: cargoType.toMap(), token: await getToken());
  }

  Future<void> deleteCargoType(String id) async {
    return await requestAPI("/cargo_types", "DELETE",
        payload: {"id": id}, token: await getToken());
  }

  Future<void> createCustomDuty(CustomDuty customDuty) async {
    return await requestAPI("/custom_duties", "POST",
        payload: customDuty.toMap(), token: await getToken());
  }

  Future<void> updateCustomDuty(CustomDuty customDuty) async {
    return await requestAPI("/custom_duties", "PUT",
        payload: customDuty.toMap(), token: await getToken());
  }

  Future<void> deleteCustomDuty(String id) async {
    return await requestAPI("/custom_duties", "DELETE",
        payload: {"id": id}, token: await getToken());
  }

  Future<void> createDiscountByWeight(DiscountByWeight discountByWeight) async {
    return await requestAPI("/discounts_by_weight", "POST",
        payload: discountByWeight.toMap(), token: await getToken());
  }

  Future<void> updateDiscountByWeight(DiscountByWeight discountByWeight) async {
    return await requestAPI("/discounts_by_weight", "PUT",
        payload: discountByWeight.toMap(), token: await getToken());
  }

  Future<void> deleteDiscountByWeight(String id) async {
    return await requestAPI("/discounts_by_weight", "DELETE",
        payload: {"id": id}, token: await getToken());
  }
}
