import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:fcs/domain/entities/cargo_type.dart';
import 'package:fcs/domain/entities/carton.dart';
import 'package:fcs/domain/entities/custom_duty.dart';
import 'package:fcs/domain/entities/discount.dart';
import 'package:fcs/domain/entities/discount_by_weight.dart';
import 'package:fcs/domain/entities/payment.dart';
import 'package:fcs/domain/entities/payment_method.dart';
import 'package:fcs/domain/entities/rate.dart';
import 'package:fcs/domain/entities/shipment.dart';

class Invoice {
  String id;
  String invoiceNumber;
  DateTime invoiceDate;
  String fcsShipmentID;
  String userID;
  String fcsID;
  String userName;
  String phoneNumber;
  String status;

  double handlingFee;
  double deliveryFee;
  double paidAmount;
  double amount;

  List<CustomDuty> customDuties;
  List<Carton> cartons;
  List<CargoType> cargoTypes;
  List<Shipment> shipments;
  List<Payment> payments;
  Discount discount;
  PaymentMethod paymentMethod;
  String invoiceURL;

  List<CargoType> getCargoTypes(Rate rate) {
    if (cargoTypes != null) return cargoTypes;

    List<CargoType> _cargoTypes = [];
    double totalCalWeight = 0;
    cartons.forEach((carton) {
      if (carton.isChecked) {
        var _cartonsTypes =
            carton.getCargoTypeForCalWeight(rate.volumetricRatio);
        _cartonsTypes.forEach((ct) {
          if (_cargoTypes.contains(ct)) {
            CargoType existing = _cargoTypes.firstWhere((wc) => wc.id == ct.id);
            existing.calWeight += ct.calWeight;
          } else {
            _cargoTypes.add(ct.clone());
          }
          totalCalWeight += ct.calWeight;
        });
      }
    });

    DiscountByWeight discountByWeight =
        rate.getDiscountByWeight(totalCalWeight);

    _cargoTypes.forEach((e) {
      double r =
          e.rate - (discountByWeight != null ? discountByWeight.discount : 0);
      e.calRate = r;
    });
    return _cargoTypes;
  }

  double getTotal(Rate rate) {
    List<CargoType> cargoTypes = getCargoTypes(rate);
    var total = cargoTypes.fold(0.0, (p, c) => c.calAmount + p);
    return total;
  }

  double get balance => (amount ?? 0) - (paidAmount ?? 0);

  double getNetAmount(Rate rate) {
    List<CargoType> cargoTypes = getCargoTypes(rate);
    var total = cargoTypes.fold(0.0, (p, c) => c.calAmount + p);
    total += getCustomFee();
    total += getDeliveryFee();
    total += getHandlingFee();
    total -= getDiscount();
    return total;
  }

  double getHandlingFee() {
    return shipments?.where((sh) => sh.isSelected ?? false)?.fold(0, (p, s) {
      return p + (s?.handlingFee ?? 0) - (s?.paidHandlingFee ?? 0);
    });
  }

  double getTotalBalance(Rate rate) {
    return getNetAmount(rate) - (paidAmount ?? 0);
  }

  double getCustomFee() {
    return customDuties == null ? 0 : customDuties.fold(0, (p, d) => p + d.fee);
  }

  double getDeliveryFee() {
    return deliveryFee == null ? 0 : deliveryFee;
  }

  double getDiscount() => discount == null ? 0 : discount.amount;

  Invoice(
      {this.id,
      this.invoiceNumber,
      this.invoiceDate,
      this.fcsID,
      this.userName,
      this.phoneNumber,
      this.amount,
      this.paidAmount,
      this.discount,
      this.status,
      this.customDuties,
      this.cartons,
      this.cargoTypes,
      this.handlingFee,
      this.deliveryFee,
      this.fcsShipmentID,
      this.shipments,
      this.invoiceURL,
      this.payments,
      this.paymentMethod});

  factory Invoice.fromMap(Map<String, dynamic> map, String docID) {
    var invd = (map['invoice_date'] as Timestamp);
    var cargoTypesMaps =
        List<Map<String, dynamic>>.from(map['cargo_types'] ?? []);
    var cargoTypes =
        cargoTypesMaps.map((e) => CargoType.fromMap(e, e["id"])).toList();
    var customDutiesMap =
        List<Map<String, dynamic>>.from(map['custom_duties'] ?? []);
    var customDuties =
        customDutiesMap.map((e) => CustomDuty.fromMap(e, e["id"])).toList();
    var handlingShipmentsMap =
        List<Map<String, dynamic>>.from(map['handling_fee_shipments'] ?? []);
    var handingShipments =
        handlingShipmentsMap.map((e) => Shipment.fromMap(e, e["id"])).toList();
    var cartonsMap = List<Map<String, dynamic>>.from(map['cartons'] ?? []);
    var cartons = cartonsMap.map((e) => Carton.fromMap(e, e["id"])).toList();
    var paymentMethodMap = map['payment_method'];
    var paymentMethod = paymentMethodMap != null
        ? PaymentMethod.fromMap(paymentMethodMap, paymentMethodMap['id'])
        : null;
    var discountMap = map['discount'];
    var discount = Discount.fromMap(discountMap, discountMap['id']);
    var paymentMaps = List<Map<String, dynamic>>.from(map['payments'] ?? []);
    var payments = paymentMaps.map((e) => Payment.fromMap(e, e["id"])).toList();
    return Invoice(
      id: docID,
      invoiceNumber: map['invoice_number'],
      invoiceDate: invd?.toDate(),
      userName: map['user_name'],
      fcsID: map['fcs_id'],
      phoneNumber: map['phone_number'],
      amount: map['amount'],
      paidAmount: double.tryParse(map['paid_amount'].toString()) ?? 0,
      status: map['status'],
      cartons: cartons,
      cargoTypes: cargoTypes,
      shipments: handingShipments,
      customDuties: customDuties,
      deliveryFee: map['delivery_fee'],
      invoiceURL: map['invoice_url'],
      paymentMethod: paymentMethod,
      discount: discount,
      payments: payments,
    );
  }

  Map<String, dynamic> toMap() {
    List _cargoTypes = cargoTypes.map((c) => c.toMap()).toList();
    List _customDuties = customDuties?.map((c) => c.toMap())?.toList();
    List _cartons = cartons?.map((c) => c.toMap())?.toList() ?? [];
    List _shipments = shipments?.map((s) => s.toMap())?.toList() ?? [];
    return {
      "id": id,
      "invoice_date": invoiceDate?.toUtc()?.toIso8601String(),
      "user_id": userID,
      "user_name": userName,
      "invoice_number": invoiceNumber,
      'fcs_shipment_id': fcsShipmentID,
      'cargo_types': _cargoTypes,
      'custom_duties': _customDuties,
      'handling_fee_shipments': _shipments,
      'cartons': _cartons,
      'discount': discount?.toMap(),
      'amount': amount,
      'handling_fee': handlingFee,
      'delivery_fee': deliveryFee,
      'invoice_url': invoiceURL,
      'payment_method': paymentMethod?.toMap(),
    };
  }
}
