import 'package:esc_pos_utils_plus/esc_pos_utils_plus.dart';
import 'package:flutter/services.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:http/http.dart' as http;
import 'package:image/image.dart' as img;
import 'package:image/image.dart';
import 'package:internet_connection_checker_plus/internet_connection_checker_plus.dart';
import 'package:intl/intl.dart';
import 'package:nb_utils/nb_utils.dart';
import 'package:print_bluetooth_thermal/print_bluetooth_thermal.dart';

import '../Provider/network_image.dart';
import '../WAVE PRINTER/wave_printer_functions.dart';
import '../constant.dart';
import '../model/add_to_cart_model.dart';
import '../model/print_transaction_model.dart';

class SalesInvoiceTharmal {
  Future<void> printSalesTicket(
      {required PrintTransactionModel printTransactionModel,
      required List<AddToCartModel>? productList}) async {
    bool? isConnected = await PrintBluetoothThermal.connectionStatus;
    if (isConnected == true) {
      List<int> bytes = (printTransactionModel.personalInformationModel.thermalPrinterPrintSize ==
                  '3 Inch (80 mm)' &&
              printTransactionModel.personalInformationModel.thermalPrinterPrintSize != null)
          ? await getSalesTicket80mm(
              printTransactionModel: printTransactionModel, productList: productList)
          : await getSalesTicket58mm(
              printTransactionModel: printTransactionModel, productList: productList);
      if (productList?.isNotEmpty ?? false) {
        await PrintBluetoothThermal.writeBytes(bytes);
        EasyLoading.showSuccess('Successfully Printed');
      } else {
        toast('No Product Found');
      }
    } else {
      EasyLoading.showError('Unable to connect with printer');
    }
  }

  Future<List<int>> getSalesTicket58mm(
      {required PrintTransactionModel printTransactionModel,
      required List<AddToCartModel>? productList}) async {
    bool isVatAddedBool = isVatAdded(products: productList ?? []);
    List<int> bytes = [];

    CapabilityProfile profile = await CapabilityProfile.load();
    final generator = Generator(PaperSize.mm58, profile);

    ///__________Shop_logo_________________________________________
    bool result = await InternetConnection().hasInternetAccess;
    if (result) {
      http.Response response = await http.get(
        Uri.parse(printTransactionModel.personalInformationModel.pictureUrl ?? ''),
      );
      response.bodyBytes;
      final decodedImage = img.decodeImage(response.bodyBytes);
      final resizedImage = img.copyResize(decodedImage!, width: 120, height: 120); //Uint8List
      final encodedImage = img.encodeJpg(resizedImage);
      Uint8List byteList = Uint8List.fromList(encodedImage);
      final Uint8List imageBytes = byteList;
      bytes += generator.image(decodeImage(imageBytes)!);
    }

    bytes += generator.text(printTransactionModel.personalInformationModel.companyName ?? '',
        styles: const PosStyles(
          align: PosAlign.center,
          height: PosTextSize.size2,
          width: PosTextSize.size2,
        ),
        linesAfter: 1);

    bytes += generator.text(printTransactionModel.personalInformationModel.countryName ?? '',
        styles: const PosStyles(align: PosAlign.center));
    bytes += generator.text(
        'Tel: ${printTransactionModel.personalInformationModel.phoneNumber ?? ''}',
        styles: const PosStyles(align: PosAlign.center),
        linesAfter: printTransactionModel.personalInformationModel.gst.trim().isNotEmpty ? 0 : 1);
    printTransactionModel.personalInformationModel.gst.trim().isNotEmpty
        ? bytes += generator.text(
            'Shop ${printTransactionModel.personalInformationModel.gstOrTinNameType == "" ? 'GST' : printTransactionModel.personalInformationModel.gstOrTinNameType}: ${printTransactionModel.personalInformationModel.gst ?? ''}',
            styles: const PosStyles(align: PosAlign.center),
            linesAfter: 1)
        : null;
    bytes += generator.text(
        'Name: ${printTransactionModel.transitionModel?.customerName ?? 'Guest'}',
        styles: const PosStyles(align: PosAlign.left));
    bytes += generator.text(
        'Mobile: ${printTransactionModel.transitionModel?.customerPhone ?? 'Not Provided'}',
        styles: const PosStyles(align: PosAlign.left));
    (printTransactionModel.transitionModel?.customerGst.trim().isNotEmpty ?? false)
        ? bytes += generator.text(
            'Party ${printTransactionModel.personalInformationModel.gstOrTinNameType == "" ? 'GST' : printTransactionModel.personalInformationModel.gstOrTinNameType}: ${printTransactionModel.transitionModel?.customerGst ?? 'Not Provided'}',
            styles: const PosStyles(align: PosAlign.left))
        : null;

    ///__________Customer_and_time_section_______________________
    bytes += generator.text(
      'Invoice: ${printTransactionModel.transitionModel?.invoiceNumber ?? 'Not Provided'}',
      styles: const PosStyles(align: PosAlign.left),
    );
    bytes += generator.text(
      'Date: ${DateFormat.yMd().format(DateTime.parse(printTransactionModel.transitionModel?.purchaseDate ?? DateTime.now().toString()))} ${DateFormat.jm().format(DateTime.parse(printTransactionModel.transitionModel?.purchaseDate ?? DateTime.now().toString()))}',
      styles: const PosStyles(align: PosAlign.left),
    );
    bytes += generator.text(
        'Sales By: ${printTransactionModel.transitionModel?.sellerName ?? 'Admin'}',
        styles: const PosStyles(align: PosAlign.left),
        linesAfter: 1);

    isVatAddedBool
        ? bytes +=
            generator.text('Items      Rate  Qty  Tax  Total', styles: const PosStyles(bold: true))
        : bytes += generator.text('Items        Rate   Qty    Total',
            styles: const PosStyles(bold: true));
    bytes += generator.hr();

    isVatAddedBool
        ? bytes += await productTable(
            productList: productList ?? [], purchaseProduct: null, isFour: false)
        : bytes += await productTable(
            productList: productList ?? [], purchaseProduct: null, isFour: true);
    bytes += generator.hr();

    ///________vat_______________________________________________
    List.generate(getAllTaxFromCartList(cart: productList ?? []).length, (index) {
      return bytes += generator.row([
        PosColumn(
            text: getAllTaxFromCartList(cart: productList ?? [])[index].name,
            width: 8,
            styles: const PosStyles(
              align: PosAlign.left,
            )),
        PosColumn(
            text: '${getAllTaxFromCartList(cart: productList ?? [])[index].taxRate.toString()}%',
            width: 4,
            styles: const PosStyles(
              align: PosAlign.right,
            )),
      ]);
    });
    bytes += generator.row([
      PosColumn(
          text: 'Subtotal',
          width: 8,
          styles: const PosStyles(
            align: PosAlign.left,
          )),
      PosColumn(
          text:
              '${formatNumberForDecimal(printTransactionModel.transitionModel!.totalAmount!.toDouble() + printTransactionModel.transitionModel!.discountAmount!.toDouble())}',
          width: 4,
          styles: const PosStyles(
            align: PosAlign.right,
          )),
    ]);

    bytes += generator.row([
      PosColumn(
          text: 'Discount',
          width: 8,
          styles: const PosStyles(
            align: PosAlign.left,
          )),
      PosColumn(
          text: formatNumberForDecimal(
                      printTransactionModel.transitionModel?.discountAmount.toString())
                  .toString() ??
              '',
          width: 4,
          styles: const PosStyles(
            align: PosAlign.right,
          )),
    ]);
    bytes += generator.hr();
    bytes += generator.row([
      PosColumn(
          text: 'TOTAL', width: 8, styles: const PosStyles(align: PosAlign.left, bold: true)),
      PosColumn(
          text: formatNumberForDecimal(printTransactionModel.transitionModel?.totalAmount)
                  .toString() ??
              '',
          width: 4,
          styles: const PosStyles(align: PosAlign.right, bold: true)),
    ]);

    // bytes += generator.hr(ch: '=', linesAfter: 1);
    // bytes += generator.hr();

    bytes += generator.row([
      PosColumn(
          text: 'Payment Type:',
          width: 8,
          styles: const PosStyles(
            align: PosAlign.left,
          )),
      PosColumn(
          text: printTransactionModel.transitionModel?.paymentType ?? 'Cash',
          width: 4,
          styles: const PosStyles(
            align: PosAlign.right,
          )),
    ]);
    bytes += generator.row([
      PosColumn(
          text: 'Payment Amount:',
          width: 8,
          styles: const PosStyles(
            align: PosAlign.left,
          )),
      PosColumn(
          text:
              '${formatNumberForDecimal(printTransactionModel.transitionModel!.totalAmount!.toDouble() - printTransactionModel.transitionModel!.dueAmount!.toDouble())}',
          width: 4,
          styles: const PosStyles(
            align: PosAlign.right,
          )),
    ]);
    if (printTransactionModel.transitionModel?.customerType != 'Guest') {
      bytes += generator.row([
        PosColumn(
          text: 'Change Amount:',
          width: 8,
          styles: const PosStyles(
            align: PosAlign.left,
          ),
        ),
        PosColumn(
            text: formatNumberForDecimal(printTransactionModel.transitionModel!.returnAmount)
                .toString(),
            width: 4,
            styles: const PosStyles(
              align: PosAlign.right,
            )),
      ]);
    }
    if (printTransactionModel.transitionModel?.customerType != 'Guest') {
      bytes += generator.row([
        PosColumn(
            text: 'Due Amount:',
            width: 8,
            styles: const PosStyles(
              align: PosAlign.left,
            )),
        PosColumn(
            text: formatNumberForDecimal(printTransactionModel.transitionModel!.dueAmount)
                .toString(),
            width: 4,
            styles: const PosStyles(
              align: PosAlign.right,
            )),
      ]);
    }
    bytes += generator.hr(ch: '=', linesAfter: 1);

    bytes +=
        generator.text('Thank you!', styles: const PosStyles(align: PosAlign.center, bold: true));

    bytes += generator.text('Note: Goods once sold will not be taken back or exchanged.',
        styles: const PosStyles(align: PosAlign.center, bold: false), linesAfter: 1);
    // bytes += generator.qrcode('https://posbharat.com', size: QRSize.Size4);
    bytes += generator.text('Developed By: $invoiceName',
        styles: const PosStyles(align: PosAlign.center), linesAfter: 1);
    bytes += generator.cut();
    return bytes;
  }

  Future<List<int>> getSalesTicket80mm(
      {required PrintTransactionModel printTransactionModel,
      required List<AddToCartModel>? productList}) async {
    bool isVatAddedBool = isVatAdded(products: productList ?? []);
    List<int> bytes = [];

    CapabilityProfile profile = await CapabilityProfile.load();

    final generator = Generator(PaperSize.mm80, profile);

    ///____________Image__________________________________
    bool result = await InternetConnection().hasInternetAccess;
    if (result) {
      final logo =
          await getNetworkImage(printTransactionModel.personalInformationModel.pictureUrl ?? '');
      if (logo != null) {
        final img.Image resized = img.copyResize(
          logo,
          width: 184,
        );
        final img.Image grayscale = img.grayscale(resized);
        bytes += generator.imageRaster(grayscale, imageFn: PosImageFn.bitImageRaster);
      }
    }

    ///____________Header______________________________________________________________
    bytes += generator.text(printTransactionModel.personalInformationModel.companyName ?? '',
        styles: const PosStyles(
          align: PosAlign.center,
          height: PosTextSize.size2,
          width: PosTextSize.size2,
        ),
        linesAfter: 1);
    if (printTransactionModel.personalInformationModel.countryName != null) {
      bytes += generator.text(
          'Address: ${printTransactionModel.personalInformationModel.countryName}',
          styles: const PosStyles(align: PosAlign.center));
    }
    if (printTransactionModel.personalInformationModel.phoneNumber != null) {
      bytes += generator.text(
          'Mobile: ${printTransactionModel.personalInformationModel.phoneNumber}',
          styles: const PosStyles(align: PosAlign.center));
    }
    bytes += generator.emptyLines(1);
    bytes += generator.text('INVOICE',
        styles: const PosStyles(
          bold: true,
          underline: true,
          align: PosAlign.center,
          height: PosTextSize.size2,
          width: PosTextSize.size2,
        ),
        linesAfter: 1);

    ///__________Customer_and_time_section_______________________
    bytes += generator.row([
      PosColumn(
          text:
              'Invoice: ${printTransactionModel.transitionModel?.invoiceNumber ?? 'Not Provided'}',
          width: 6,
          styles: const PosStyles(align: PosAlign.left)),
      PosColumn(
          text:
              'Date: ${DateFormat.yMd().format(DateTime.parse(printTransactionModel.transitionModel?.purchaseDate ?? DateTime.now().toString()))}',
          width: 6,
          styles: const PosStyles(align: PosAlign.right)),
    ]);
    bytes += generator.row([
      PosColumn(
          text: 'Name: ${printTransactionModel.transitionModel?.customerName ?? ''}',
          width: 6,
          styles: const PosStyles(align: PosAlign.left)),
      PosColumn(
          text:
              'Time: ${DateFormat.jm().format(DateTime.parse(printTransactionModel.transitionModel?.purchaseDate ?? DateTime.now().toString()))}',
          width: 6,
          styles: const PosStyles(align: PosAlign.right)),
    ]);
    bytes += generator.row([
      PosColumn(
          text: 'Mobile: ${printTransactionModel.transitionModel?.customerPhone ?? ''}',
          width: 6,
          styles: const PosStyles(align: PosAlign.left)),
      PosColumn(
          text: 'Sales By: ${printTransactionModel.transitionModel?.sellerName ?? 'Admin'}',
          width: 6,
          styles: const PosStyles(align: PosAlign.right)),
    ]);

    bytes += generator.emptyLines(1);

    ///____________Products_Section_____________________________________________________
    bytes += generator.hr();
    isVatAddedBool
        ? bytes += generator.row([
            PosColumn(
                text: 'SL', width: 1, styles: const PosStyles(align: PosAlign.left, bold: true)),
            PosColumn(
                text: 'Item', width: 4, styles: const PosStyles(align: PosAlign.left, bold: true)),
            PosColumn(
                text: 'Qty',
                width: 1,
                styles: const PosStyles(align: PosAlign.center, bold: true)),
            PosColumn(
                text: 'Price',
                width: 2,
                styles: const PosStyles(align: PosAlign.center, bold: true)),
            PosColumn(
                text: 'Tax',
                width: 2,
                styles: const PosStyles(align: PosAlign.center, bold: true)),
            PosColumn(
                text: 'Amount',
                width: 2,
                styles: const PosStyles(align: PosAlign.right, bold: true)),
          ])
        : bytes += generator.row([
            PosColumn(
                text: 'SL', width: 1, styles: const PosStyles(align: PosAlign.left, bold: true)),
            PosColumn(
                text: 'Item', width: 5, styles: const PosStyles(align: PosAlign.left, bold: true)),
            PosColumn(
                text: 'Qty',
                width: 2,
                styles: const PosStyles(align: PosAlign.center, bold: true)),
            PosColumn(
                text: 'Price',
                width: 2,
                styles: const PosStyles(align: PosAlign.center, bold: true)),
            PosColumn(
                text: 'Amount',
                width: 2,
                styles: const PosStyles(align: PosAlign.right, bold: true)),
          ]);
    bytes += generator.hr();
    isVatAddedBool
        ? List.generate(productList?.length ?? 1, (index) {
            final product = productList?[index];
            return bytes += generator.row([
              PosColumn(
                  text: '${index + 1}',
                  width: 1,
                  styles: const PosStyles(
                    align: PosAlign.left,
                  )),
              PosColumn(
                  text: product?.productName ?? '',
                  width: 4,
                  styles: const PosStyles(
                    align: PosAlign.left,
                  )),
              PosColumn(
                  text: formatPointNumber(product?.quantity ?? 0, addComma: true),
                  width: 1,
                  styles: const PosStyles(align: PosAlign.center)),
              PosColumn(
                  text: formatPointNumber(num.tryParse(product?.subTotal) ?? 0, addComma: true),
                  width: 2,
                  styles: const PosStyles(
                    align: PosAlign.center,
                  )),
              PosColumn(
                  text: calculateProductVat(product: product!),
                  width: 2,
                  styles: const PosStyles(
                    align: PosAlign.center,
                  )),
              PosColumn(
                  text: formatPointNumber(
                      num.tryParse(product.subTotal.toString() ?? '0') ??
                          0 * (num.tryParse(product.quantity.toString() ?? '0') ?? 0),
                      addComma: true),
                  width: 2,
                  styles: const PosStyles(align: PosAlign.right)),
            ]);
          })
        : List.generate(productList?.length ?? 1, (index) {
            final product = productList?[index];
            return bytes += generator.row([
              PosColumn(
                  text: '${index + 1}',
                  width: 1,
                  styles: const PosStyles(
                    align: PosAlign.left,
                  )),
              PosColumn(
                  text: product?.productName ?? '',
                  width: 5,
                  styles: const PosStyles(
                    align: PosAlign.left,
                  )),
              PosColumn(
                  text: formatPointNumber(product?.quantity ?? 0, addComma: true),
                  width: 2,
                  styles: const PosStyles(align: PosAlign.center)),
              PosColumn(
                  text: formatPointNumber(num.tryParse(product?.subTotal) ?? 0, addComma: true),
                  width: 2,
                  styles: const PosStyles(
                    align: PosAlign.center,
                  )),
              PosColumn(
                  text: formatPointNumber(
                      num.tryParse(product?.subTotal.toString() ?? '0') ??
                          0 * (num.tryParse(product?.quantity.toString() ?? '0') ?? 0),
                      addComma: true),
                  width: 2,
                  styles: const PosStyles(align: PosAlign.right)),
            ]);
          });
    bytes += generator.hr();

    ///________vat_______________________________________________
    getAllTaxFromCartList(cart: productList ?? []).forEach(
      (element) {
        bytes += generator.row([
          PosColumn(
              text: '${element.name}:',
              width: 9,
              styles: const PosStyles(
                align: PosAlign.right,
              )),
          PosColumn(
              text: '${element.taxRate.toString()}%',
              width: 3,
              styles: const PosStyles(
                align: PosAlign.right,
              )),
        ]);
      },
    );

    bytes += generator.row([
      PosColumn(
          text: 'Sub-total:',
          width: 9,
          styles: const PosStyles(
            align: PosAlign.right,
          )),
      PosColumn(
          text: formatPointNumber(
              (printTransactionModel.transitionModel?.totalAmount ?? 0) +
                  (printTransactionModel.transitionModel?.discountAmount ?? 0),
              addComma: true),
          width: 3,
          styles: const PosStyles(
            align: PosAlign.right,
          )),
    ]);
    bytes += generator.row([
      PosColumn(
          text: 'Discount:',
          width: 9,
          styles: const PosStyles(
            align: PosAlign.right,
          )),
      PosColumn(
        text: formatPointNumber((printTransactionModel.transitionModel?.discountAmount ?? 0),
            addComma: true),
        width: 3,
        styles: const PosStyles(
          align: PosAlign.right,
        ),
      ),
    ]);

    bytes += generator.row([
      PosColumn(
          text: 'Total:',
          width: 9,
          styles: const PosStyles(
            align: PosAlign.right,
          )),
      PosColumn(
          text: (formatPointNumber(printTransactionModel.transitionModel?.totalAmount ?? 0,
              addComma: true)),
          width: 3,
          styles: const PosStyles(
            align: PosAlign.right,
          )),
    ]);

    bytes += generator.text('                    ----------------------------');

    bytes += generator.row([
      PosColumn(
          text: 'Total Payable:',
          width: 9,
          styles: const PosStyles(align: PosAlign.right, bold: true)),
      PosColumn(
          text: formatPointNumber(printTransactionModel.transitionModel?.totalAmount ?? 0,
              addComma: true),
          width: 3,
          styles: const PosStyles(align: PosAlign.right, bold: true)),
    ]);
    bytes += generator.row([
      PosColumn(
          text: 'Paid Amount:',
          width: 9,
          styles: const PosStyles(
            align: PosAlign.right,
          )),
      PosColumn(
          text: formatPointNumber(
              printTransactionModel.transitionModel!.totalAmount!.toDouble() -
                  printTransactionModel.transitionModel!.dueAmount!.toDouble(),
              addComma: true),
          width: 3,
          styles: const PosStyles(
            align: PosAlign.right,
          )),
    ]);
    if ((printTransactionModel.transitionModel?.dueAmount ?? 0) > 0) {
      bytes += generator.row([
        PosColumn(
          text: 'Due Amount:',
          width: 9,
          styles: const PosStyles(
            align: PosAlign.right,
          ),
        ),
        PosColumn(
            text: formatPointNumber(printTransactionModel.transitionModel?.dueAmount ?? 0,
                addComma: true),
            width: 3,
            styles: const PosStyles(
              align: PosAlign.right,
            )),
      ]);
    } else {
      if (printTransactionModel.transitionModel?.customerType != 'Guest') {
        bytes += generator.row([
          PosColumn(
            text: 'Change Amount:',
            width: 9,
            styles: const PosStyles(
              align: PosAlign.right,
            ),
          ),
          PosColumn(
              text: formatNumberForDecimal(printTransactionModel.transitionModel!.returnAmount)
                  .toString(),
              width: 3,
              styles: const PosStyles(
                align: PosAlign.right,
              )),
        ]);
      }
    }
    bytes += generator.hr();

    bytes += generator.text(
      'Payment Type: ${printTransactionModel.transitionModel?.paymentType ?? 'N/A'}',
      linesAfter: 1,
    );

    bytes += generator.text('Thank you!',
        styles: const PosStyles(align: PosAlign.center, bold: true), linesAfter: 1);

    bytes += generator.text('Note: Goods once sold will not be taken back or exchanged.',
        styles: const PosStyles(align: PosAlign.center, bold: false), linesAfter: 1);
    bytes += generator.qrcode(pdfFooter, size: QRSize.size4);
    bytes += generator.text('Developed By: $invoiceName',
        styles: const PosStyles(align: PosAlign.center), linesAfter: 1);
    bytes += generator.cut();

    return bytes;
  }
}
