import 'dart:developer';

import 'package:dartz/dartz.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:injectable/injectable.dart';
import 'package:print_bluetooth_thermal/print_bluetooth_thermal.dart';

import '../../../domain/printer/printer.dart';
import '../datasource/local_data_provider.dart';
import '../printer_dtos.dart';

@Injectable(as: IPrinterRepository)
class PrinterRepository implements IPrinterRepository {
  final PrinterLocalDataProvider _localDataProvider;
  final _logName = 'PrinterRepository';
  PrinterRepository(this._localDataProvider);

  @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(
        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(
        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(
        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(
        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());
    }
  }
}
