import 'dart:async';

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:fcs/data/services/services.dart';
import 'package:fcs/domain/constants.dart';
import 'package:fcs/domain/entities/shipment.dart';
import 'package:fcs/helpers/paginator.dart';
import 'package:fcs/pages/main/model/base_model.dart';
import 'package:logging/logging.dart';

class ShipmentModel extends BaseModel {
  final log = Logger('ShipmentModel');

  StreamSubscription<QuerySnapshot> listener;

  List<Shipment> get shipments => _menuSelectedIndex == 1
      ? _shipments
      : List<Shipment>.from(_delivered.values);

  List<Shipment> _shipments = [];

  Paginator _delivered;
  bool isLoading = false;
  int _menuSelectedIndex = 1;

  set menuSelectedIndex(int index) {
    _menuSelectedIndex = index;
    notifyListeners();
  }

  get menuSelectedIndex => _menuSelectedIndex;

  initData(bool forCustomer, {bool myPickup = false}) {
    logout();
    _loadShipments(forCustomer, myPickup);
    _delivered = _getDelivered(forCustomer);
    _delivered.load(onFinished: () {
      isLoading = false;
      notifyListeners();
    });
  }

  @override
  logout() async {
    if (_delivered != null) _delivered.close();
    if (listener != null) await listener.cancel();
    _shipments = [];
  }

  Future<void> loadMore({bool isCustomer}) async {
    if (menuSelectedIndex == 1)
      return; // when delivered menu is not selected return
    if (_delivered.ended) return;
    isLoading = true;
    notifyListeners();
    await _delivered.load(onFinished: () {
      isLoading = false;
      notifyListeners();
    });
  }

  Future<void> refresh({bool isCustomer}) async {
    if (menuSelectedIndex == 1)
      return; // when delivered menu is not selected return
    await _delivered.refresh(onFinished: () {
      notifyListeners();
    });
  }

  Paginator _getDelivered(bool isCustomer) {
    if (!isCustomer) {
      if (user == null ||
          !((user.hasPackages() ||
              user.hasReceiving() ||
              user.hasProcessing()))) throw "No privilege";
    }
    var pageQuery = Firestore.instance
        .collection("/$shipments_collection")
        .where("is_delivered", isEqualTo: true)
        .where("is_deleted", isEqualTo: false);
    if (isCustomer) {
      pageQuery = pageQuery.where("user_id", isEqualTo: user.id);
    }
    pageQuery = pageQuery.orderBy("status_date", descending: true);
    var paginator = new Paginator(pageQuery, rowPerLoad: 20, toObj: (data, id) {
      return Shipment.fromMap(data, id);
    });
    return paginator;
  }

  Future<void> _loadShipments(bool forCustomer, bool myPickup) async {
    if (user == null) return;
    if (!forCustomer && !user.hasShipment()) return;
    if (listener != null) listener.cancel();
    _shipments = [];

    try {
      var q = Firestore.instance
          .collection("$shipments_collection")
          .where("is_delivered", isEqualTo: false)
          .where("is_canceled", isEqualTo: false)
          .where("is_deleted", isEqualTo: false);

      if (forCustomer) {
        q = q.where("user_id", isEqualTo: user.id);
      }
      if (myPickup) {
        q = q.where("pickup_user_id", isEqualTo: user.id);
      }
      q = q.orderBy("created_at", descending: true);

      listener = q.snapshots().listen((QuerySnapshot snapshot) {
        _shipments.clear();
        _shipments = snapshot.documents.map((documentSnapshot) {
          var s = Shipment.fromMap(
              documentSnapshot.data, documentSnapshot.documentID);
          return s;
        }).toList();
        notifyListeners();
      });
    } catch (e) {
      log.warning("Error!! $e");
    }
  }

  List<String> shipmentTypes = [
    shipment_local_pickup,
    shipment_courier_pickup,
    shipment_local_dropoff,
    shipment_courier_dropoff
  ];

  Shipment getActiveShipment(String shipmentID) {
    return _shipments?.firstWhere((e) => e.id == shipmentID,
        orElse: () => null);
  }

  Future<Shipment> getShipment(String shipmentID) async {
    String path = "/$shipments_collection";
    try {
      var ref = Firestore.instance.collection("$path").document(shipmentID);
      var snap = await ref.get(source: Source.server);
      if (snap.exists) {
        var s = Shipment.fromMap(snap.data, snap.documentID);
        return s;
      }
    } catch (e) {
      log.warning("Error!! $e");
    }
    return null;
  }

  Future<List<Shipment>> getShipmentWithHandlingFee(
      String fcsShipmentID, String userID) async {
    String path = "/$shipments_collection";
    try {
      var q = Firestore.instance
          .collection("$path")
          .where("user_id", isEqualTo: userID)
          .where("is_deleted", isEqualTo: false)
          .where("handling_fee", isGreaterThan: 0)
          .where("fcs_shipment_id", isEqualTo: fcsShipmentID);
      var snaps = await q.getDocuments(source: Source.server);
      List<Shipment> shipments = snaps.documents.map((snap) {
        if (snap.exists) {
          var s = Shipment.fromMap(snap.data, snap.documentID);
          return s;
        }
      }).toList();
      return shipments;
    } catch (e) {
      log.warning("Error!! $e");
    }
    return null;
  }

  void initUser(user) {
    super.initUser(user);
  }

  Future<void> createShipment(Shipment shipment) {
    return Services.instance.shipmentService.createShipment(shipment);
  }

  Future<void> updateShipment(Shipment shipment) {
    return Services.instance.shipmentService.updateShipment(shipment);
  }

  Future<void> cancelShipment(Shipment shipment) {
    return Services.instance.shipmentService.cancelShipment(shipment);
  }

  Future<void> assignShipment(Shipment shipment) {
    return Services.instance.shipmentService.assignShipment(shipment);
  }

  Future<void> completeAssignShipment(Shipment shipment) {
    return Services.instance.shipmentService.completeAssignShipment(shipment);
  }

  Future<void> completePickupShipment(Shipment shipment) {
    return Services.instance.shipmentService.completePickupShipment(shipment);
  }

  Future<void> completePackShipment(Shipment shipment) {
    return Services.instance.shipmentService.completePackShipment(shipment);
  }

  Future<void> packShipment(Shipment shipment) {
    return Services.instance.shipmentService.packShipment(shipment);
  }

  Future<void> confirmShipment(Shipment shipment) async {
    // String path = Path.join(shipment_labels_files_path);
    // String url = await uploadStorage(path, file);
    // shipment.shipmentLabelUrl = url;
    return Services.instance.shipmentService.confirmShipment(shipment);
  }

  Future<void> completeConfirmShipment(Shipment shipment) {
    return Services.instance.shipmentService.completeConfirmShipment(shipment);
  }

  Future<void> completeReceiveShipment(Shipment shipment) {
    return Services.instance.shipmentService.completeReceiveShipment(shipment);
  }
}
