import "package:flutter/material.dart";
import "package:flutter_speed_dial/flutter_speed_dial.dart";

import "package:flutter_tabler_icons/flutter_tabler_icons.dart";

import "package:inventree/app_colors.dart";
import "package:inventree/barcode/barcode.dart";
import "package:inventree/barcode/stock.dart";
import "package:inventree/helpers.dart";
import "package:inventree/inventree/attachment.dart";
import "package:inventree/inventree/sales_order.dart";
import "package:inventree/l10.dart";
import "package:inventree/api.dart";
import "package:inventree/api_form.dart";
import "package:inventree/labels.dart";
import "package:inventree/preferences.dart";

import "package:inventree/inventree/company.dart";
import "package:inventree/inventree/stock.dart";
import "package:inventree/inventree/part.dart";

import "package:inventree/widget/dialogs.dart";
import "package:inventree/widget/attachment_widget.dart";
import "package:inventree/widget/link_icon.dart";
import "package:inventree/widget/progress.dart";
import "package:inventree/widget/refreshable_state.dart";
import "package:inventree/widget/snacks.dart";
import "package:inventree/widget/stock/stock_item_history.dart";
import "package:inventree/widget/stock/stock_item_test_results.dart";
import "package:inventree/widget/notes_widget.dart";

class StockDetailWidget extends StatefulWidget {
  const StockDetailWidget(this.item, {Key? key}) : super(key: key);

  final InvenTreeStockItem item;

  @override
  _StockItemDisplayState createState() => _StockItemDisplayState();
}

class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
  _StockItemDisplayState();

  @override
  String getAppBarTitle() => L10().stockItem;

  bool stockShowHistory = false;
  bool stockShowTests = true;
  bool expiryEnabled = false;

  // Linked data fields
  InvenTreePart? part;
  InvenTreeStockLocation? defaultLocation;
  InvenTreeSalesOrder? salesOrder;
  InvenTreeCompany? customer;

  @override
  List<Widget> appBarActions(BuildContext context) {
    List<Widget> actions = [];

    if (api.supportsMixin("locate")) {
      actions.add(
        IconButton(
          icon: Icon(Icons.travel_explore),
          tooltip: L10().locateItem,
          onPressed: () async {
            api.locateItemOrLocation(context, item: widget.item.pk);
          },
        ),
      );
    }

    if (widget.item.canEdit) {
      actions.add(
        IconButton(
          icon: Icon(TablerIcons.edit),
          tooltip: L10().editItem,
          onPressed: () {
            _editStockItem(context);
          },
        ),
      );
    }

    return actions;
  }

  @override
  List<SpeedDialChild> actionButtons(BuildContext context) {
    List<SpeedDialChild> actions = [];

    if (widget.item.canEdit) {
      // Stock adjustment actions available if item is *not* serialized
      if (!widget.item.isSerialized()) {
        actions.add(
          SpeedDialChild(
            child: Icon(TablerIcons.circle_check, color: COLOR_ACTION),
            label: L10().countStock,
            onTap: _countStockDialog,
          ),
        );

        actions.add(
          SpeedDialChild(
            child: Icon(TablerIcons.circle_minus, color: COLOR_DANGER),
            label: L10().removeStock,
            onTap: _removeStockDialog,
          ),
        );

        actions.add(
          SpeedDialChild(
            child: Icon(TablerIcons.circle_plus, color: COLOR_SUCCESS),
            label: L10().addStock,
            onTap: _addStockDialog,
          ),
        );
      }

      // Transfer item
      actions.add(
        SpeedDialChild(
          child: Icon(TablerIcons.transfer),
          label: L10().transferStock,
          onTap: () {
            _transferStockDialog(context);
          },
        ),
      );
    }

    if (allowLabelPrinting && api.supportsModernLabelPrinting) {
      actions.add(
        SpeedDialChild(
          child: Icon(TablerIcons.printer),
          label: L10().printLabel,
          onTap: () async {
            selectAndPrintLabel(context, "stockitem", widget.item.pk);
          },
        ),
      );
    }

    if (widget.item.canDelete) {
      actions.add(
        SpeedDialChild(
          child: Icon(TablerIcons.trash, color: COLOR_DANGER),
          label: L10().stockItemDelete,
          onTap: () {
            _deleteItem(context);
          },
        ),
      );
    }

    return actions;
  }

  @override
  List<SpeedDialChild> barcodeButtons(BuildContext context) {
    List<SpeedDialChild> actions = [];

    if (widget.item.canEdit) {
      // Scan item into location
      actions.add(
        SpeedDialChild(
          child: Icon(Icons.qr_code_scanner),
          label: L10().scanIntoLocation,
          onTap: () {
            scanBarcode(
              context,
              handler: StockItemScanIntoLocationHandler(widget.item),
            ).then((ctx) {
              refresh(context);
            });
          },
        ),
      );

      actions.add(
        customBarcodeAction(
          context,
          this,
          widget.item.customBarcode,
          "stockitem",
          widget.item.pk,
        ),
      );
    }

    return actions;
  }

  bool allowLabelPrinting = false;
  int attachmentCount = 0;

  @override
  Future<void> onBuild(BuildContext context) async {
    // Load part data if not already loaded
    if (part == null) {
      refresh(context);
    }
  }

  @override
  Future<void> request(BuildContext context) async {
    await api.StockStatus.load();
    stockShowHistory =
        await InvenTreeSettingsManager().getValue(INV_STOCK_SHOW_HISTORY, false)
            as bool;
    stockShowTests =
        await InvenTreeSettingsManager().getValue(INV_STOCK_SHOW_TESTS, true)
            as bool;

    final bool result = widget.item.pk > 0 && await widget.item.reload();

    // Could not load this stock item for some reason
    // Perhaps it has been depleted?
    if (!result) {
      Navigator.of(context).pop();
    }

    expiryEnabled = await api.getGlobalBooleanSetting("STOCK_ENABLE_EXPIRY");

    // Request part information
    part = await InvenTreePart().get(widget.item.partId) as InvenTreePart?;

    stockShowTests &= part?.isTestable ?? false;

    // Request default location
    int? defaultLocationId = part?.defaultLocation;

    if (defaultLocationId != null) {
      InvenTreeStockLocation().get(defaultLocationId).then((value) {
        if (mounted) {
          setState(() {
            defaultLocation = value as InvenTreeStockLocation?;
          });
        }
      });
    } else if (mounted) {
      setState(() {
        defaultLocation = null;
      });
    }

    // Request test results (async)
    if (stockShowTests) {
      widget.item.getTestResults().then((value) {
        if (mounted) {
          setState(() {
            // Update
          });
        }
      });
    }

    // Request the number of attachments
    InvenTreeAttachment()
        .countAttachments(InvenTreeStockItem.MODEL_TYPE, widget.item.pk)
        .then((int value) {
          if (mounted) {
            setState(() {
              attachmentCount = value;
            });
          }
        });

    // Request SalesOrder information
    if (widget.item.hasSalesOrder) {
      InvenTreeSalesOrder()
          .get(widget.item.salesOrderId)
          .then(
            (instance) => {
              if (mounted)
                {
                  setState(() {
                    salesOrder = instance as InvenTreeSalesOrder?;
                  }),
                },
            },
          );
    } else {
      if (mounted) {
        setState(() {
          salesOrder = null;
        });
      }
    }

    // Request Customer information
    if (widget.item.hasCustomer) {
      InvenTreeCompany()
          .get(widget.item.customerId)
          .then(
            (instance) => {
              if (mounted)
                {
                  setState(() {
                    customer = instance as InvenTreeCompany?;
                  }),
                },
            },
          );
    } else {
      if (mounted) {
        setState(() {
          customer = null;
        });
      }
    }

    allowLabelPrinting = await InvenTreeSettingsManager().getBool(
      INV_ENABLE_LABEL_PRINTING,
      true,
    );
  }

  /// Delete the stock item from the database
  Future<void> _deleteItem(BuildContext context) async {
    confirmationDialog(
      L10().stockItemDelete,
      L10().stockItemDeleteConfirm,
      icon: TablerIcons.trash,
      color: COLOR_DANGER,
      acceptText: L10().delete,
      onAccept: () async {
        final bool result = await widget.item.delete();

        if (result) {
          Navigator.of(context).pop();
          showSnackIcon(L10().stockItemDeleteSuccess, success: true);
        } else {
          showSnackIcon(L10().stockItemDeleteFailure, success: false);
        }
      },
    );
  }

  Future<void> _editStockItem(BuildContext context) async {
    var fields = InvenTreeStockItem().formFields();

    // Some fields we don't want to edit!
    fields.remove("part");
    fields.remove("quantity");
    fields.remove("location");
    fields.remove("serial_numbers");

    if (part == null || !part!.isTrackable) {
      fields.remove("serial");
    }

    widget.item.editForm(
      context,
      L10().editItem,
      fields: fields,
      onSuccess: (data) async {
        refresh(context);
        showSnackIcon(L10().stockItemUpdated, success: true);
      },
    );
  }

  /*
   * Launch a dialog to 'add' quantity to this StockItem
   */
  Future<void> _addStockDialog() async {
    Map<String, dynamic> fields = {
      "pk": {
        "parent": "items",
        "nested": true,
        "hidden": true,
        "value": widget.item.pk,
      },
      "quantity": {"parent": "items", "nested": true, "value": 0},
      "notes": {},
    };

    launchApiForm(
      context,
      L10().addStock,
      InvenTreeStockItem.addStockUrl(),
      fields,
      method: "POST",
      icon: TablerIcons.circle_plus,
      onSuccess: (data) async {
        _stockUpdateMessage(true);
        refresh(context);
      },
    );
  }

  void _stockUpdateMessage(bool result) {
    if (result) {
      showSnackIcon(L10().stockItemUpdated, success: true);
    }
  }

  /*
   * Launch a dialog to 'remove' quantity from this StockItem
   */
  void _removeStockDialog() {
    Map<String, dynamic> fields = {
      "pk": {
        "parent": "items",
        "nested": true,
        "hidden": true,
        "value": widget.item.pk,
      },
      "quantity": {"parent": "items", "nested": true, "value": 0},
      "notes": {},
    };

    launchApiForm(
      context,
      L10().removeStock,
      InvenTreeStockItem.removeStockUrl(),
      fields,
      method: "POST",
      icon: TablerIcons.circle_minus,
      onSuccess: (data) async {
        _stockUpdateMessage(true);
        refresh(context);
      },
    );
  }

  Future<void> _countStockDialog() async {
    Map<String, dynamic> fields = {
      "pk": {
        "parent": "items",
        "nested": true,
        "hidden": true,
        "value": widget.item.pk,
      },
      "quantity": {
        "parent": "items",
        "nested": true,
        "value": widget.item.quantity,
      },
      "notes": {},
    };

    launchApiForm(
      context,
      L10().countStock,
      InvenTreeStockItem.countStockUrl(),
      fields,
      method: "POST",
      icon: TablerIcons.clipboard_check,
      onSuccess: (data) async {
        _stockUpdateMessage(true);
        refresh(context);
      },
    );
  }

  /*
   * Launches an API Form to transfer this stock item to a new location
   */
  Future<void> _transferStockDialog(BuildContext context) async {
    Map<String, dynamic> fields = widget.item.transferFields();

    launchApiForm(
      context,
      L10().transferStock,
      InvenTreeStockItem.transferStockUrl(),
      fields,
      method: "POST",
      icon: TablerIcons.transfer,
      onSuccess: (data) async {
        _stockUpdateMessage(true);
        refresh(context);
      },
    );
  }

  Widget headerTile() {
    Widget? trailing;

    if (!widget.item.isInStock) {
      trailing = LargeText(L10().unavailable, color: COLOR_DANGER);
    } else if (!widget.item.isSerialized()) {
      trailing = LargeText(
        widget.item.quantityString(),
        color: api.StockStatus.color(widget.item.status),
      );
    }

    return Card(
      child: ListTile(
        title: Text(widget.item.partName),
        subtitle: Text(widget.item.partDescription),
        leading: InvenTreeAPI().getThumbnail(widget.item.partImage),
        trailing: trailing,
        onTap: () async {
          if (widget.item.partId > 0) {
            showLoadingOverlay();
            var part = await InvenTreePart().get(widget.item.partId);
            hideLoadingOverlay();

            if (part is InvenTreePart) {
              part.goToDetailPage(context);
            }
          }
        },
        //trailing: LargeText(item.serialOrQuantityDisplay()),
      ),
    );
  }

  /*
   * Construct a list of detail elements about this StockItem.
   * The number of elements may vary depending on the StockItem details
   */
  @override
  List<Widget> getTiles(BuildContext context) {
    List<Widget> tiles = [];

    // Image / name / description
    tiles.add(headerTile());

    if (loading) {
      tiles.add(progressIndicator());
      return tiles;
    }

    if (showPk) {
      tiles.add(pkTile(widget.item.pk));
    }

    // Location information
    if ((widget.item.locationId > 0) && (widget.item.locationName.isNotEmpty)) {
      tiles.add(
        ListTile(
          title: Text(L10().stockLocation),
          subtitle: Text("${widget.item.locationPathString}"),
          leading: Icon(TablerIcons.location, color: COLOR_ACTION),
          trailing: LinkIcon(),
          onTap: () async {
            if (widget.item.locationId > 0) {
              showLoadingOverlay();
              var loc = await InvenTreeStockLocation().get(
                widget.item.locationId,
              );
              hideLoadingOverlay();

              if (loc is InvenTreeStockLocation) {
                loc.goToDetailPage(context);
              }
            }
          },
        ),
      );
    } else {
      tiles.add(
        ListTile(
          title: Text(L10().stockLocation),
          leading: Icon(TablerIcons.location),
          subtitle: Text(L10().locationNotSet),
        ),
      );
    }

    if (defaultLocation != null &&
        defaultLocation?.pk != widget.item.locationId) {
      tiles.add(
        ListTile(
          title: Text(L10().locationDefault),
          subtitle: Text(defaultLocation!.pathstring),
          leading: Icon(TablerIcons.map_pin),
          trailing: LinkIcon(),
          onTap: () {
            defaultLocation?.goToDetailPage(context);
          },
        ),
      );
    }

    // Quantity information
    if (widget.item.isSerialized()) {
      tiles.add(
        ListTile(
          title: Text(L10().serialNumber),
          leading: Icon(TablerIcons.hash),
          subtitle: Text("${widget.item.serialNumber}"),
        ),
      );
    } else if (widget.item.isInStock) {
      tiles.add(
        ListTile(
          title: widget.item.allocated > 0
              ? Text(L10().quantityAvailable)
              : Text(L10().quantity),
          leading: Icon(TablerIcons.packages),
          trailing: LargeText("${widget.item.quantityString()}"),
        ),
      );
    }

    if (!widget.item.isInStock) {
      tiles.add(
        ListTile(
          leading: Icon(TablerIcons.box_off),
          title: Text(
            L10().unavailable,
            style: TextStyle(color: COLOR_DANGER, fontWeight: FontWeight.bold),
          ),
          subtitle: Text(
            L10().unavailableDetail,
            style: TextStyle(color: COLOR_DANGER),
          ),
        ),
      );
    }

    // Stock item status information
    tiles.add(
      ListTile(
        title: Text(L10().status),
        leading: Icon(TablerIcons.help_circle),
        trailing: LargeText(
          api.StockStatus.label(widget.item.status),
          color: api.StockStatus.color(widget.item.status),
        ),
      ),
    );

    // Supplier part information (if available)
    if (widget.item.supplierPartId > 0) {
      tiles.add(
        ListTile(
          title: Text(L10().supplierPart),
          subtitle: Text(widget.item.supplierSKU),
          leading: Icon(TablerIcons.building, color: COLOR_ACTION),
          trailing: LinkIcon(),
          onTap: () async {
            showLoadingOverlay();
            var sp = await InvenTreeSupplierPart().get(
              widget.item.supplierPartId,
            );
            hideLoadingOverlay();
            if (sp is InvenTreeSupplierPart) {
              sp.goToDetailPage(context);
            }
          },
        ),
      );
    }

    if (widget.item.isBuilding) {
      tiles.add(
        ListTile(
          title: Text(L10().inProduction),
          leading: Icon(TablerIcons.tools),
          subtitle: Text(L10().inProductionDetail),
          onTap: () {
            // TODO: Click through to the "build order"
          },
        ),
      );
    }

    if (widget.item.hasSalesOrder && salesOrder != null) {
      tiles.add(
        ListTile(
          title: Text(L10().salesOrder),
          subtitle: Text(
            salesOrder?.reference ?? salesOrder?.description ?? "",
          ),
          leading: Icon(TablerIcons.truck_delivery, color: COLOR_ACTION),
          trailing: LinkIcon(),
          onTap: () {
            salesOrder?.goToDetailPage(context);
          },
        ),
      );
    }

    if (widget.item.hasCustomer && customer != null) {
      tiles.add(
        ListTile(
          title: Text(L10().customer),
          subtitle: Text("${customer?.name} - ${customer?.description}"),
          leading: Icon(TablerIcons.building_store, color: COLOR_ACTION),
          trailing: LinkIcon(),
          onTap: () {
            customer?.goToDetailPage(context);
          },
        ),
      );
    }

    if (widget.item.batch.isNotEmpty) {
      tiles.add(
        ListTile(
          title: Text(L10().batchCode),
          subtitle: Text(widget.item.batch),
          leading: Icon(TablerIcons.clipboard_text),
        ),
      );
    }

    if (widget.item.packaging.isNotEmpty) {
      tiles.add(
        ListTile(
          title: Text(L10().packaging),
          subtitle: Text(widget.item.packaging),
          leading: Icon(TablerIcons.package),
        ),
      );
    }

    if (expiryEnabled && widget.item.expiryDate != null) {
      Widget? _expiryIcon;

      if (widget.item.expired) {
        _expiryIcon = Text(
          L10().expiryExpired,
          style: TextStyle(color: COLOR_DANGER),
        );
      } else if (widget.item.stale) {
        _expiryIcon = Text(
          L10().expiryStale,
          style: TextStyle(color: COLOR_WARNING),
        );
      }

      tiles.add(
        ListTile(
          title: Text(L10().expiryDate),
          subtitle: Text(widget.item.expiryDateString),
          leading: Icon(TablerIcons.calendar_x),
          trailing: _expiryIcon,
        ),
      );
    }

    if (api.supportsStockItemCreationDate &&
        widget.item.creationDateString.isNotEmpty) {
      tiles.add(
        ListTile(
          title: Text(L10().creationDate),
          trailing: LargeText(widget.item.creationDateString),
          leading: Icon(TablerIcons.calendar_plus),
        ),
      );
    }

    // Last update?
    if (widget.item.updatedDateString.isNotEmpty) {
      tiles.add(
        ListTile(
          title: Text(L10().lastUpdated),
          trailing: LargeText(widget.item.updatedDateString),
          leading: Icon(TablerIcons.calendar),
        ),
      );
    }

    // Stocktake?
    if (widget.item.stocktakeDateString.isNotEmpty) {
      tiles.add(
        ListTile(
          title: Text(L10().lastStocktake),
          trailing: LargeText(widget.item.stocktakeDateString),
          leading: Icon(TablerIcons.calendar),
        ),
      );
    }

    if (widget.item.hasLink) {
      tiles.add(
        ListTile(
          title: Text("${widget.item.link}"),
          leading: Icon(TablerIcons.link, color: COLOR_ACTION),
          onTap: () {
            widget.item.openLink();
          },
        ),
      );
    }

    if (stockShowTests || (widget.item.testResultCount > 0)) {
      tiles.add(
        ListTile(
          title: Text(L10().testResults),
          leading: Icon(TablerIcons.list_check, color: COLOR_ACTION),
          trailing: LinkIcon(text: "${widget.item.testResultCount}"),
          onTap: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => StockItemTestResultsWidget(widget.item),
              ),
            ).then((ctx) {
              refresh(context);
            });
          },
        ),
      );
    }

    if (widget.item.hasPurchasePrice) {
      tiles.add(
        ListTile(
          title: Text(L10().purchasePrice),
          leading: Icon(TablerIcons.currency_dollar),
          trailing: LargeText(
            renderCurrency(
              widget.item.purchasePrice,
              widget.item.purchasePriceCurrency,
            ),
          ),
        ),
      );
    }

    // TODO - Is this stock item linked to a PurchaseOrder?

    if (stockShowHistory && widget.item.trackingItemCount > 0) {
      tiles.add(
        ListTile(
          title: Text(L10().history),
          leading: Icon(TablerIcons.history, color: COLOR_ACTION),
          trailing: LinkIcon(text: "${widget.item.trackingItemCount}"),
          onTap: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => StockItemHistoryWidget(widget.item),
              ),
            ).then((ctx) {
              refresh(context);
            });
          },
        ),
      );
    }

    // Notes field
    tiles.add(
      ListTile(
        title: Text(L10().notes),
        leading: Icon(TablerIcons.note, color: COLOR_ACTION),
        trailing: LinkIcon(),
        onTap: () {
          Navigator.push(
            context,
            MaterialPageRoute(builder: (context) => NotesWidget(widget.item)),
          );
        },
      ),
    );

    ListTile? attachmentTile = ShowAttachmentsItem(
      context,
      InvenTreeStockItem.MODEL_TYPE,
      widget.item.pk,
      L10().stockItem,
      attachmentCount,
      widget.item.canEdit,
    );

    if (attachmentTile != null) {
      tiles.add(attachmentTile);
    }

    return tiles;
  }
}
