import 'dart:developer';

import 'package:dartz/dartz.dart' hide Order;
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter_esc_pos_network/flutter_esc_pos_network.dart';
import 'package:injectable/injectable.dart' hide Order;
import 'package:print_bluetooth_thermal/print_bluetooth_thermal.dart';
import 'package:synchronized/synchronized.dart';

import '../../../common/extension/extension.dart';
import '../../../domain/order/order.dart';
import '../../../domain/outlet/outlet.dart';
import '../../../domain/printer/printer.dart';
import '../../../presentation/components/print/print_ui.dart';
import '../../auth/datasources/local_data_provider.dart';
import '../../outlet/datasources/local_data_provider.dart';
import '../datasource/local_data_provider.dart';
import '../printer_dtos.dart';

@Injectable(as: IPrinterRepository)
class PrinterRepository implements IPrinterRepository {
  final PrinterLocalDataProvider _localDataProvider;
  final OutletLocalDatasource _outletLocalDatasource;
  final AuthLocalDataProvider _authLocalDataProvider;
  final _logName = 'PrinterRepository';

  PrinterRepository(
    this._localDataProvider,
    this._outletLocalDatasource,
    this._authLocalDataProvider,
  );

  final Map<String, Lock> _locks = {};
  final Lock _globalPrintLock = Lock();
  String? _connectedMac;

  Lock _getLock(String address) {
    return _locks.putIfAbsent(address, () => Lock());
  }

  @override
  Future<Either<PrinterFailure, bool>> connectBluetooth(
    String macAddress,
  ) async {
    try {
      bool isConnected = await PrintBluetoothThermal.connectionStatus;
      if (isConnected) {
        log("Already connected to Bluetooth printer", name: _logName);
        return right(true);
      }

      bool connected = await PrintBluetoothThermal.connect(
        macPrinterAddress: macAddress,
      );

      if (connected) {
        log(
          "Successfully connected to Bluetooth printer: $macAddress",
          name: _logName,
        );
      } else {
        FirebaseCrashlytics.instance.recordError(
          'Failed to connect to Bluetooth printer',
          null,
          reason: 'Failed to connect to Bluetooth printe',
          information: [
            'function: connectBluetoothPrinter(String macAddress)',
            'macAddress: $macAddress',
          ],
        );
        log(
          "Failed to connect to Bluetooth printer: $macAddress",
          name: _logName,
        );
      }

      return right(connected);
    } catch (e, stackTrace) {
      FirebaseCrashlytics.instance.recordError(
        e,
        stackTrace,
        reason: 'Error connecting to Bluetooth printer',
        information: [
          'function: connectBluetoothPrinter(String macAddress)',
          'Printer: Bluetooth printer',
          'macAddress: $macAddress',
        ],
      );
      log("Error connecting to Bluetooth printer", name: _logName, error: e);
      return left(
        const PrinterFailure.dynamicErrorMessage(
          'Error connecting to Bluetooth printer',
        ),
      );
    }
  }

  @override
  Future<Either<PrinterFailure, bool>> disconectBluetooth() async {
    try {
      bool result = await PrintBluetoothThermal.disconnect;
      log("Bluetooth printer disconnected: $result", name: _logName);
      return right(result);
    } catch (e) {
      log("Error disconnecting Bluetooth printer", error: e, name: _logName);
      return left(
        const PrinterFailure.dynamicErrorMessage(
          'Error disconnecting Bluetooth printer',
        ),
      );
    }
  }

  @override
  Future<Either<PrinterFailure, List<BluetoothInfo>>>
  getPairedBluetoothDevices() async {
    try {
      final result = await PrintBluetoothThermal.pairedBluetooths;

      log("Paired Bluetooth devices: $result", name: _logName);

      return right(result);
    } catch (e) {
      log("Error getting paired Bluetooth devices", name: _logName, error: e);
      return left(
        const PrinterFailure.dynamicErrorMessage(
          'Error getting paired Bluetooth devices',
        ),
      );
    }
  }

  @override
  Future<Either<PrinterFailure, bool>> isBluetoothEnabled() async {
    try {
      final result = await PrintBluetoothThermal.bluetoothEnabled;

      return right(result);
    } catch (e) {
      log("Error checking Bluetooth status", name: _logName, error: e);
      return left(
        const PrinterFailure.dynamicErrorMessage(
          'Error checking Bluetooth status',
        ),
      );
    }
  }

  @override
  Future<Either<PrinterFailure, Unit>> createPrinter(Printer printer) async {
    try {
      final result = await _localDataProvider.createPrinter(
        PrinterDto.fromDomain(printer),
      );

      if (result.hasError) {
        return left(result.error!);
      }

      return right(unit);
    } catch (e) {
      log('createPrinterError', name: _logName, error: e);
      return left(const PrinterFailure.unexpectedError());
    }
  }

  @override
  Future<Either<PrinterFailure, Unit>> deletePrinter(int id) async {
    try {
      final result = await _localDataProvider.deletePrinter(id);

      if (result.hasError) {
        return left(result.error!);
      }

      return right(unit);
    } catch (e) {
      log('deletePrinterError', name: _logName, error: e);
      return left(const PrinterFailure.unexpectedError());
    }
  }

  @override
  Future<Either<PrinterFailure, Printer>> getPrinterByCode(String code) async {
    try {
      final result = await _localDataProvider.findPrinterByCode(code);

      if (result.hasError) {
        return left(result.error!);
      }

      final printer = result.data!.toDomain();

      return right(printer);
    } catch (e) {
      log('getPrinterByCodeError', name: _logName, error: e);
      return left(const PrinterFailure.unexpectedError());
    }
  }

  @override
  Future<Either<PrinterFailure, Unit>> updatePrinter(
    Printer printer,
    int id,
  ) async {
    try {
      final result = await _localDataProvider.updatePrinter(
        PrinterDto.fromDomain(printer),
        id,
      );

      if (result.hasError) {
        return left(result.error!);
      }

      return right(unit);
    } catch (e) {
      log('updatePrinterError', name: _logName, error: e);
      return left(const PrinterFailure.unexpectedError());
    }
  }

  @override
  Future<Either<PrinterFailure, bool>> printStruct(
    Printer printer,
    List<int> printData,
  ) async {
    if (printer.type == 'Bluetooth') {
      return await _getLock(printer.address).synchronized(() async {
        try {
          bool isConnected = await PrintBluetoothThermal.connectionStatus;

          // Hanya reconnect jika memang perlu
          if (!isConnected || _connectedMac != printer.address) {
            // Disconnect hanya jika terhubung ke printer LAIN
            if (isConnected && _connectedMac != printer.address) {
              await PrintBluetoothThermal.disconnect;
              await Future.delayed(
                const Duration(milliseconds: 300),
              ); // dari 1500ms
            }

            bool connected = await PrintBluetoothThermal.connect(
              macPrinterAddress: printer.address,
            );

            if (!connected) {
              _connectedMac = null;
              FirebaseCrashlytics.instance.recordError(
                'Failed to connect to Bluetooth printer',
                null,
                reason: 'Failed to connect to Bluetooth print',
                information: [
                  'function: printStruct(Printer printer, List<int> printData)',
                  'Printer: ${printer.name}',
                  'macAddress: ${printer.address}',
                  'in: $_logName',
                ],
              );
              return left(
                const PrinterFailure.dynamicErrorMessage(
                  'Printer cannot connect to bluetooth, Please connect in printer setting!',
                ),
              );
            }

            _connectedMac = printer.address;
            await Future.delayed(
              const Duration(milliseconds: 300),
            ); // dari 1000ms
          }

          log(
            'Printer connected to bluetooth: ${printer.name}, ${printer.address}',
            name: _logName,
          );

          bool printResult = await _writeBytesChunked(printData);

          // Hapus delay 1000ms setelah print — tidak perlu untuk hasil cetak
          log("Print result ${printer.name}: $printResult");

          if (!printResult) {
            FirebaseCrashlytics.instance.recordError(
              'Failed to print to ${printer.name}',
              null,
              information: [
                'function: await _writeBytesChunked(printData)',
                'print: $printData',
                'in: $_logName',
              ],
            );
            return left(
              PrinterFailure.dynamicErrorMessage(
                'Failed to print to ${printer.name}',
              ),
            );
          }

          return right(printResult);
        } catch (e, stackTrace) {
          _connectedMac = null;
          FirebaseCrashlytics.instance.recordError(
            e,
            stackTrace,
            reason: 'Error printing ${printer.name}',
            information: [
              'Printer: ${printer.name}',
              'MAC: ${printer.address}',
            ],
          );
          return left(
            const PrinterFailure.dynamicErrorMessage('Error printing struct'),
          );
        }
      });
    } else {
      // Network printer tetap sama
      try {
        bool printResult = await _printNetwork(printer.address, printData);

        if (!printResult) {
          FirebaseCrashlytics.instance.recordError(
            'Failed to connect to Network Printer',
            null,
            reason: 'Failed to connect to Network Printer',
            information: [
              'function: await _printNetwork(printer.address, printData)',
              'Printer: ${printer.name}',
              'ipAddress: ${printer.address}',
              'print: $printData',
              'in: $_logName',
            ],
          );
        }

        return right(printResult);
      } catch (e, stackTrace) {
        FirebaseCrashlytics.instance.recordError(
          e,
          stackTrace,
          reason: 'Error printing ${printer.name}',
          information: ['Printer: ${printer.name}', 'IP: ${printer.address}'],
        );
        return left(
          const PrinterFailure.dynamicErrorMessage('Error printing struct'),
        );
      }
    }
  }
  // Future<bool> _printBluetooth(List<int> printData) async {
  //   try {
  //     bool isConnected = await PrintBluetoothThermal.connectionStatus;

  //     if (!isConnected) {
  //       log("Not connected to Bluetooth printer", name: _logName);
  //       return false;
  //     }

  //     bool printResult = await PrintBluetoothThermal.writeBytes(printData);
  //     if (printResult) {
  //       log("Successfully printed via Bluetooth", name: _logName);
  //     } else {
  //       FirebaseCrashlytics.instance.recordError(
  //         'Failed to print via Bluetooth',
  //         null,
  //         reason: 'Failed to print via Bluetooth',
  //         information: [
  //           'function: printBluetooth(List<int> printData)',
  //           'printData: $printData',
  //           'in: $_logName',
  //         ],
  //       );
  //       log("Failed to print via Bluetooth", name: _logName);
  //     }

  //     return printResult;
  //   } catch (e, stackTrace) {
  //     FirebaseCrashlytics.instance.recordError(
  //       e,
  //       stackTrace,
  //       reason: 'Error printing via Bluetooth',
  //       information: [
  //         'function: printBluetooth(List<int> printData)',
  //         'Printer: Bluetooth printer',
  //         'printData: $printData',
  //         'in: $_logName',
  //       ],
  //     );
  //     log("Error printing via Bluetooth: $e", name: _logName);
  //     return false;
  //   }
  // }

  Future<bool> _printNetwork(String ipAddress, List<int> printData) async {
    try {
      final printer = PrinterNetworkManager(
        ipAddress,
        port: 9100,
        timeout: const Duration(seconds: 15),
      );
      PosPrintResult connect = await printer.connect();

      if (connect == PosPrintResult.success) {
        PosPrintResult printing = await printer.printTicket(printData);
        printer.disconnect();

        if (printing == PosPrintResult.success) {
          log("Successfully printed via Network printer: $ipAddress");
          return true;
        } else {
          FirebaseCrashlytics.instance.recordError(
            'Failed to print via Network printer: ${printing.msg}',
            null,
            reason: 'Failed to print via Network printer',
            information: [
              'function: printNetwork(String ipAddress, List<int> printData)',
              'Printer: Network printer',
              'ipAddress: $ipAddress',
              'printData: $printData',
            ],
          );
          log("Failed to print via Network printer: ${printing.msg}");
          return false;
        }
      } else {
        FirebaseCrashlytics.instance.recordError(
          'Failed to connect to Network printer: ${connect.msg}',
          null,
          reason: 'Failed to connectNetwork printer',
          information: [
            'function: printNetwork(String ipAddress, List<int> printData)',
            'Printer: Network printer',
            'ipAddress: $ipAddress',
            'printData: $printData',
          ],
        );
        log("Failed to connect to Network printer: ${connect.msg}");
        return false;
      }
    } catch (e, stackTrace) {
      FirebaseCrashlytics.instance.recordError(
        e,
        stackTrace,
        reason: 'Error printing via Network',
        information: [
          'function: printNetwork(String ipAddress, List<int> printData)',
          'Printer: Network printer',
          'ipAddress: $ipAddress',
          'printData: $printData',
        ],
      );
      log("Error printing via Network: $e");
      return false;
    }
  }

  @override
  Future<Either<PrinterFailure, Unit>> printStruckOrder({
    required Order order,
  }) async {
    return await _globalPrintLock.synchronized(() async {
    try {
      final outlet = await _outletLocalDatasource.currentOutlet();
      final user = await _authLocalDataProvider.currentUser();

      // Struck for customer
      if (order.totalPaid > 0) {
        final result = await _printReceipt(
          order: order,
          outlet: outlet,
          cashieName: user.name,
        );
        if (result.isLeft()) return result; // Jika gagal, return error
      }

      // Struck for cashier
      final cashierResult = await _printCashier(
        order: order,
        outlet: outlet,
        cashieName: user.name,
      );
      if (cashierResult.isLeft()) return cashierResult;

      // Struck for bar if exist product bar
      final itemsKitchen = order.orderItems.where(
        (item) => item.printerType == 'kitchen',
      );

      if (itemsKitchen.isNotEmpty) {
        final kitchenResult = await _printKitchen(
          order: order,
          outlet: outlet,
          cashieName: user.name,
        );
        if (kitchenResult.isLeft()) return kitchenResult;
      }

      // Struck for bar if exist product bar
      final itemsBar = order.orderItems.where(
        (item) => item.printerType == 'bar',
      );

      if (itemsBar.isNotEmpty) {
        final barResult = await _printBar(
          order: order,
          outlet: outlet,
          cashieName: user.name,
        );
        if (barResult.isLeft()) return barResult;
      }

      // Struck for checker
      final checkerResult = await _printChecker(
        order: order,
        outlet: outlet,
        cashieName: user.name,
      );
      if (checkerResult.isLeft()) return checkerResult;

      return right(unit);
    } catch (e, stackTrace) {
      FirebaseCrashlytics.instance.recordError(
        'Print Struck Order Error: $e',
        stackTrace,
        reason:
            'Print struck order error / Printer not setting in printer page',
        information: ['Order ID: ${order.id}'],
      );
      log("Error printing struck order", name: _logName, error: e);
      return left(
        const PrinterFailure.dynamicErrorMessage('Error printing order'),
      );
    }
    }); // end _globalPrintLock.synchronized
  }

  @override
  Future<Either<PrinterFailure, Unit>> printStruckSaveOrder({
    required Order order,
  }) async {
    return await _globalPrintLock.synchronized(() async {
    try {
      final outlet = await _outletLocalDatasource.currentOutlet();
      final user = await _authLocalDataProvider.currentUser();

      // Struck for cashier
      final cashierResult = await _printCashier(
        order: order,
        outlet: outlet,
        cashieName: user.name,
      );
      if (cashierResult.isLeft()) return cashierResult;

      // Struck for kitchen
      // Struck for bar if exist product bar
      final itemsKitchen = order.orderItems.where(
        (item) => item.printerType == 'kitchen',
      );

      if (itemsKitchen.isNotEmpty) {
        final kitchenResult = await _printKitchen(
          order: order,
          outlet: outlet,
          cashieName: user.name,
        );
        if (kitchenResult.isLeft()) return kitchenResult;
      }

      // Struck for bar if exist product bar
      final itemsBar = order.orderItems.where(
        (item) => item.printerType == 'bar',
      );

      if (itemsBar.isNotEmpty) {
        final barResult = await _printBar(
          order: order,
          outlet: outlet,
          cashieName: user.name,
        );
        if (barResult.isLeft()) return barResult;
      }

      // Struck for checker
      final checkerResult = await _printChecker(
        order: order,
        outlet: outlet,
        cashieName: user.name,
      );
      if (checkerResult.isLeft()) return checkerResult;

      return right(unit);
    } catch (e, stackTrace) {
      FirebaseCrashlytics.instance.recordError(
        'Print Struck Save Order Error: $e',
        stackTrace,
        reason:
            'Print struck save order error / Printer not setting in printer page',
        information: ['Order ID: ${order.id}'],
      );
      log("Error printing struck save order", name: _logName, error: e);
      return left(
        const PrinterFailure.dynamicErrorMessage('Error printing save order'),
      );
    }
    }); // end _globalPrintLock.synchronized
  }

  @override
  Future<Either<PrinterFailure, Unit>> printStruckCashier({
    required Order order,
  }) async {
    return await _globalPrintLock.synchronized(() async {
    final outlet = await _outletLocalDatasource.currentOutlet();
    final user = await _authLocalDataProvider.currentUser();
    return _printCashier(order: order, outlet: outlet, cashieName: user.name);
    });
  }

  @override
  Future<Either<PrinterFailure, Unit>> printStruckVoid({
    required Order order,
  }) async {
    return await _globalPrintLock.synchronized(() async {
    final outlet = await _outletLocalDatasource.currentOutlet();
    final user = await _authLocalDataProvider.currentUser();
    return _printVoid(order: order, outlet: outlet, cashieName: user.name);
    });
  }

  @override
  Future<Either<PrinterFailure, Unit>> printStruckSplitBill({
    required Order order,
  }) async {
    return await _globalPrintLock.synchronized(() async {
    final outlet = await _outletLocalDatasource.currentOutlet();
    final user = await _authLocalDataProvider.currentUser();
    return _printSplitBill(order: order, outlet: outlet, cashieName: user.name);
    });
  }

  @override
  Future<Either<PrinterFailure, Unit>> printStruckPayment({
    required Order order,
  }) async {
    return await _globalPrintLock.synchronized(() async {
    final outlet = await _outletLocalDatasource.currentOutlet();
    final user = await _authLocalDataProvider.currentUser();
    return _printReceipt(order: order, outlet: outlet, cashieName: user.name);
    });
  }

  Future<Either<PrinterFailure, Unit>> _printReceipt({
    required Order order,
    required Outlet outlet,
    required String cashieName,
  }) async {
    log('Starting to print receipt', name: _logName);
    final receiptPrinter = await _localDataProvider.findPrinterByCode(
      'receipt',
    );
    if (receiptPrinter.hasData) {
      try {
        final printer = receiptPrinter.data!.toDomain();

        final printValue = await PrintUi().printOrder(
          order: order,
          outlet: outlet,
          cashierName: cashieName,
          paper: printer.paper.toIntegerFromText,
        );

        final printResult = await printStruct(printer, printValue);
        if (printResult.isLeft()) return printResult.map((_) => unit);

        log('Finished printed receipt', name: _logName);
        log('Printer Size: ', name: printer.paper);

        return right(unit);
      } catch (e, stackTrace) {
        FirebaseCrashlytics.instance.recordError(
          'Print Struck Receipt Error: $e',
          stackTrace,
          reason:
              'Print struck receipt error / Printer not setting in printer page',
          information: ['Order ID: ${order.id}'],
        );
        log("Error printing receipt", name: _logName, error: e);
        return left(
          const PrinterFailure.dynamicErrorMessage(
            'Error printing receipt order',
          ),
        );
      }
    } else {
      log('Receipt printer not found', name: _logName);
      FirebaseCrashlytics.instance.recordError(
        'Receipt printer not found',
        null,
        reason:
            'Receipt printer not found / Printer not setting in printer page',
        information: ['Order ID: ${order.id}'],
      );
      return left(
        const PrinterFailure.dynamicErrorMessage(
          'Receipt printer belum terhubung, Silahkan hubungkan printer di pengaturan.',
        ),
      );
    }
  }

  Future<Either<PrinterFailure, Unit>> _printCashier({
    required Order order,
    required Outlet outlet,
    required String cashieName,
  }) async {
    log('Starting to print cashier', name: _logName);
    final receiptPrinter = await _localDataProvider.findPrinterByCode(
      'receipt',
    );
    if (receiptPrinter.hasData) {
      try {
        final printer = receiptPrinter.data!.toDomain();

        final printValue = await PrintUi().printCashier(
          order: order,
          outlet: outlet,
          cashierName: cashieName,
          paper: printer.paper.toIntegerFromText,
        );

        final printResult = await printStruct(printer, printValue);
        if (printResult.isLeft()) return printResult.map((_) => unit);

        log('Finished printing cashier', name: _logName);

        return right(unit);
      } catch (e, stackTrace) {
        FirebaseCrashlytics.instance.recordError(
          'Print Struck Cashier Error: $e',
          stackTrace,
          reason:
              'Print struck cashier error / Printer not setting in printer page',
          information: ['Order ID: ${order.id}'],
        );
        log("Error printing cashier", name: _logName, error: e);
        return left(
          const PrinterFailure.dynamicErrorMessage(
            'Error printing cashier order',
          ),
        );
      }
    } else {
      FirebaseCrashlytics.instance.recordError(
        'Cashier printer not found',
        null,
        reason:
            'Cashier printer not found / Printer not setting in printer page',
        information: ['Order ID: ${order.id}'],
      );
      return left(
        const PrinterFailure.dynamicErrorMessage(
          'Receipt printer belum terhubung, Silahkan hubungkan printer di pengaturan.',
        ),
      );
    }
  }

  Future<Either<PrinterFailure, Unit>> _printKitchen({
    required Order order,
    required Outlet outlet,
    required String cashieName,
  }) async {
    log('Starting to print kitchen', name: _logName);
    final kitchenPrinter = await _localDataProvider.findPrinterByCode(
      'kitchen',
    );
    if (kitchenPrinter.hasData) {
      try {
        final productKitchen = order.orderItems
            .where((item) => item.printerType == 'kitchen')
            .toList();

        if (productKitchen.isNotEmpty) {
          final printer = kitchenPrinter.data!.toDomain();

          final printValue = await PrintUi().printKitchen(
            order: order.copyWith(orderItems: productKitchen),
            outlet: outlet,
            cashierName: cashieName,
            paper: printer.paper.toIntegerFromText,
          );

          final printResult = await printStruct(printer, printValue);
          if (printResult.isLeft()) return printResult.map((_) => unit);
        } else {
          log('Product with printer type bar not found', name: _logName);
        }

        log('Finished printed kitchen', name: _logName);

        return right(unit);
      } catch (e, stackTrace) {
        FirebaseCrashlytics.instance.recordError(
          'Print Struck Kitchen Error: $e',
          stackTrace,
          reason:
              'Print struck kitchen error / Printer not setting in printer page',
          information: ['Order ID: ${order.id}'],
        );
        log("Error printing kitchen", name: _logName, error: e);
        return left(
          const PrinterFailure.dynamicErrorMessage(
            'Error printing kitchen order',
          ),
        );
      }
    } else {
      FirebaseCrashlytics.instance.recordError(
        'Kitchen printer not found',
        null,
        reason:
            'Kitchen printer not found / Printer not setting in printer page',
        information: ['Order ID: ${order.id}'],
      );
      return left(
        const PrinterFailure.dynamicErrorMessage(
          'Kitchen printer belum terhubung, Silahkan hubungkan printer di pengaturan.',
        ),
      );
    }
  }

  Future<Either<PrinterFailure, Unit>> _printChecker({
    required Order order,
    required Outlet outlet,
    required String cashieName,
  }) async {
    log('Starting to print checker', name: _logName);
    final checkerPrinter = await _localDataProvider.findPrinterByCode(
      'checker',
    );
    if (checkerPrinter.hasData) {
      try {
        final printer = checkerPrinter.data!.toDomain();

        final printValue = await PrintUi().printChecker(
          order: order,
          outlet: outlet,
          cashierName: cashieName,
          paper: printer.paper.toIntegerFromText,
        );

        final printResult = await printStruct(printer, printValue);
        if (printResult.isLeft()) return printResult.map((_) => unit);

        log('Finished printed checker', name: _logName);

        return right(unit);
      } catch (e, stackTrace) {
        FirebaseCrashlytics.instance.recordError(
          'Print Struck Checker Error: $e',
          stackTrace,
          reason:
              'Print struck checker error / Printer not setting in printer page',
          information: ['Order ID: ${order.id}'],
        );
        log("Error printing checker", name: _logName, error: e);
        return left(
          const PrinterFailure.dynamicErrorMessage(
            'Error printing checker order',
          ),
        );
      }
    } else {
      log('Checker printer not found', name: _logName);
      FirebaseCrashlytics.instance.recordError(
        'Checker printer not found',
        null,
        reason:
            'Checker printer not found / Printer not setting in printer page',
        information: ['Order ID: ${order.id}'],
      );
      return left(
        const PrinterFailure.dynamicErrorMessage(
          'Checker printer belum terhubung, Silahkan hubungkan printer di pengaturan.',
        ),
      );
    }
  }

  Future<Either<PrinterFailure, Unit>> _printBar({
    required Order order,
    required Outlet outlet,
    required String cashieName,
  }) async {
    final barPrinter = await _localDataProvider.findPrinterByCode('bar');
    if (barPrinter.hasData) {
      log('Starting to print bar', name: _logName);
      try {
        final productBar = order.orderItems
            .where((item) => item.printerType == 'bar')
            .toList();

        if (productBar.isNotEmpty) {
          final printer = barPrinter.data!.toDomain();

          final printValue = await PrintUi().printBar(
            order: order.copyWith(orderItems: productBar),
            outlet: outlet,
            cashierName: cashieName,
            paper: printer.paper.toIntegerFromText,
          );

          final printResult = await printStruct(printer, printValue);
          if (printResult.isLeft()) return printResult.map((_) => unit);

          log('Finished printed bar', name: _logName);
        } else {
          log('Product with printer type bar not found', name: _logName);
          return right(unit);
        }

        return right(unit);
      } catch (e, stackTrace) {
        FirebaseCrashlytics.instance.recordError(
          'Print Struck Bar Error: $e',
          stackTrace,
          reason:
              'Print struck bar error / Printer not setting in printer page',
          information: ['Order ID: ${order.id}'],
        );
        log("Error printing bar", name: _logName, error: e);
        return left(
          const PrinterFailure.dynamicErrorMessage('Error printing bar order'),
        );
      }
    } else {
      FirebaseCrashlytics.instance.recordError(
        'Bar printer not found',
        null,
        reason: 'Bar printer not found / Printer not setting in printer page',
        information: ['Order ID: ${order.id}'],
      );
      return left(
        const PrinterFailure.dynamicErrorMessage(
          'Bar printer belum terhubung, Silahkan hubungkan printer di pengaturan.',
        ),
      );
    }
  }

  Future<Either<PrinterFailure, Unit>> _printVoid({
    required Order order,
    required Outlet outlet,
    required String cashieName,
  }) async {
    log('Starting to print void', name: _logName);
    final voidPrinter = await _localDataProvider.findPrinterByCode('receipt');
    if (voidPrinter.hasData) {
      try {
        final printer = voidPrinter.data!.toDomain();

        final printValue = await PrintUi().printVoid(
          order: order,
          outlet: outlet,
          cashierName: cashieName,
          paper: printer.paper.toIntegerFromText,
        );

        final printResult = await printStruct(printer, printValue);
        if (printResult.isLeft()) return printResult.map((_) => unit);

        log('Finished printed void', name: _logName);

        return right(unit);
      } catch (e, stackTrace) {
        FirebaseCrashlytics.instance.recordError(
          'Print Struck Void Error: $e',
          stackTrace,
          reason:
              'Print struck void error / Printer not setting in printer page',
          information: ['Order ID: ${order.id}'],
        );
        log("Error printing void", name: _logName, error: e);
        return left(
          const PrinterFailure.dynamicErrorMessage('Error printing void order'),
        );
      }
    } else {
      FirebaseCrashlytics.instance.recordError(
        'Void printer not found',
        null,
        reason: 'Void printer not found / Printer not setting in printer page',
        information: ['Order ID: ${order.id}'],
      );
      return left(
        const PrinterFailure.dynamicErrorMessage(
          'Receipt printer belum terhubung, Silahkan hubungkan printer di pengaturan.',
        ),
      );
    }
  }

  Future<Either<PrinterFailure, Unit>> _printSplitBill({
    required Order order,
    required Outlet outlet,
    required String cashieName,
  }) async {
    log('Starting to print splitBill', name: _logName);
    final splitBillPrinter = await _localDataProvider.findPrinterByCode(
      'receipt',
    );
    if (splitBillPrinter.hasData) {
      try {
        final printer = splitBillPrinter.data!.toDomain();

        final printValue = await PrintUi().printSplit(
          order: order,
          outlet: outlet,
          cashierName: cashieName,
          paper: printer.paper.toIntegerFromText,
        );

        final printResult = await printStruct(printer, printValue);
        if (printResult.isLeft()) return printResult.map((_) => unit);

        log('Finished printed splitBill', name: _logName);

        return right(unit);
      } catch (e, stackTrace) {
        FirebaseCrashlytics.instance.recordError(
          'Print Struck SplitBill Error: $e',
          stackTrace,
          reason:
              'Print struck splitBill error / Printer not setting in printer page',
          information: ['Order ID: ${order.id}'],
        );
        log("Error printing splitBill", name: _logName, error: e);
        return left(
          const PrinterFailure.dynamicErrorMessage(
            'Error printing splitBill order',
          ),
        );
      }
    } else {
      FirebaseCrashlytics.instance.recordError(
        'SplitBill printer not found',
        null,
        reason:
            'SplitBill printer not found / Printer not setting in printer page',
        information: ['Order ID: ${order.id}'],
      );
      return left(
        const PrinterFailure.dynamicErrorMessage(
          'Receipt printer belum terhubung, Silahkan hubungkan printer di pengaturan.',
        ),
      );
    }
  }

  Future<bool> _writeBytesChunked(List<int> data, {int chunkSize = 512}) async {
    for (int i = 0; i < data.length; i += chunkSize) {
      final end = (i + chunkSize > data.length) ? data.length : i + chunkSize;
      final chunk = data.sublist(i, end);
      bool result = await PrintBluetoothThermal.writeBytes(chunk);
      if (!result) return false;
      await Future.delayed(const Duration(milliseconds: 100));
    }
    return true;
  }

  @override
  Future<Either<PrinterFailure, Unit>> printStruckBar({
    required Order order,
  }) async {
    return await _globalPrintLock.synchronized(() async {
    final outlet = await _outletLocalDatasource.currentOutlet();
    final user = await _authLocalDataProvider.currentUser();
    return _printBar(order: order, outlet: outlet, cashieName: user.name);
    });
  }

  @override
  Future<Either<PrinterFailure, Unit>> printStruckChecker({
    required Order order,
  }) async {
    return await _globalPrintLock.synchronized(() async {
    final outlet = await _outletLocalDatasource.currentOutlet();
    final user = await _authLocalDataProvider.currentUser();
    return _printChecker(order: order, outlet: outlet, cashieName: user.name);
    });
  }

  @override
  Future<Either<PrinterFailure, Unit>> printStruckKitchen({
    required Order order,
  }) async {
    return await _globalPrintLock.synchronized(() async {
    final outlet = await _outletLocalDatasource.currentOutlet();
    final user = await _authLocalDataProvider.currentUser();
    return _printKitchen(order: order, outlet: outlet, cashieName: user.name);
    });
  }
}
