import 'dart:async';
import 'dart:io';

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/invoice.dart';
import 'package:fcs/domain/entities/payment.dart';
import 'package:fcs/helpers/firebase_helper.dart';
import 'package:fcs/helpers/paginator.dart';
import 'package:fcs/pages/main/model/base_model.dart';
import 'package:logging/logging.dart';
import 'package:path/path.dart' as Path;

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

  StreamSubscription<QuerySnapshot> listener;

  List<Invoice> _invoices = [];

  List<Invoice> get invoices =>
      _selectedIndex == 1 ? _invoices : List<Invoice>.from(_paginator.values);

  Paginator _paginator;

  bool endOfPaidInvoices = false;
  bool isLoading = false;
  int _selectedIndex = 1;

  set selectedIndex(int index) {
    _selectedIndex = index;
    notifyListeners();
  }

  get selectedIndex => _selectedIndex;

  @override
  void privilegeChanged() {
    super.privilegeChanged();
  }

  initData(bool forCustomer, bool isCanceled, bool isPaid) {
    _loadInvoices(forCustomer);

    if (_paginator != null) _paginator.close();
    _paginator = _getPaginator(forCustomer, isCanceled, isPaid);
    _paginator.load(onFinished: () {
      notifyListeners();
    });
  }

  Future<void> _loadInvoices(bool forCustomer) async {
    if (user == null) return;
    if (!forCustomer && !user.hasInvoices()) return;
    String path = "/$invoices_collection";
    if (listener != null) listener.cancel();
    _invoices = [];

    try {
      var q = Firestore.instance
          .collection("$path")
          .where("status", isEqualTo: invoice_issued_status)
          .where("is_deleted", isEqualTo: false);

      if (forCustomer) {
        q = q.where("user_id", isEqualTo: user.id);
      }

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

  Paginator _getPaginator(bool isCustomer, bool isCanceled, bool isPaid) {
    if (!isCustomer) {
      if (user == null || !(user.hasInvoices())) throw "No privilege";
    }
    var pageQuery = Firestore.instance
        .collection("/$invoices_collection")
        .where("is_deleted", isEqualTo: false);
    if (isCustomer) {
      pageQuery = pageQuery.where("user_id", isEqualTo: user.id);
    }
    if (isCanceled) {
      pageQuery = pageQuery.where("status", isEqualTo: invoice_cancel_status);
    }
    if (isPaid) {
      pageQuery = pageQuery.where("status", isEqualTo: invoice_paid_status);
    }

    pageQuery = pageQuery.orderBy("created_at", descending: true);
    var paginator = new Paginator(pageQuery, rowPerLoad: 20, toObj: (data, id) {
      return Invoice.fromMap(data, id);
    });
    return paginator;
  }

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

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

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

  logout() async {
    if (_paginator != null) _paginator.close();
    if (listener != null) await listener.cancel();
    _invoices = [];
  }

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

  Future<void> pay(Payment payment, File file) async {
    String path = Path.join(receipt_labels_files_path, user.id);
    String url = await uploadStorage(path, file);
    payment.paymentReceiptURL = url;
    return Services.instance.invoiceService.pay(payment);
  }

  Future<void> updatePaymentStatus(Payment payment) async {
    return Services.instance.invoiceService.updatPaymentStatus(payment);
  }

  Future<void> createInvoice(Invoice invoice) async {
    return Services.instance.invoiceService.createInvoice(invoice);
  }

  Future<void> updateInvoice(Invoice invoice) {
    return Services.instance.invoiceService.updateInvoice(invoice);
  }

  Future<void> cancelInvoice(Invoice invoice) {
    return Services.instance.invoiceService.cancelInvoice(invoice);
  }
}
