#import "Ibeacon.h"
#import <CoreBluetooth/CoreBluetooth.h>
#import <React/RCTLog.h>

@interface Ibeacon () <CBCentralManagerDelegate>
@property (nonatomic, strong) CBCentralManager *centralManager;
@property (nonatomic, assign) BOOL hasListeners;
@end

@implementation Ibeacon

RCT_EXPORT_MODULE();

- (NSArray<NSString *> *)supportedEvents {
  return @[@"ScanResult", @"ScanError", @"IbeaconLocalAddress"];
}

- (void)startObserving {
  self.hasListeners = YES;
  [self startScanning];
}

- (void)stopObserving {
  self.hasListeners = NO;
  [self stopScanning];
}

RCT_EXPORT_METHOD(multiply:(double)a b:(double)b resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
  double result = a * b;
  resolve(@(result));
}

- (void)startScanning {
  if (!self.centralManager) {
    self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:@{CBCentralManagerOptionShowPowerAlertKey:@YES}];
  } else {
    // Se já estiver pronto, inicia o scan
    [self.centralManager scanForPeripheralsWithServices:nil options:@{ CBCentralManagerScanOptionAllowDuplicatesKey : @YES }];
    if (self.hasListeners) {
      // Em BLE puro não há um "localAddress", assim enviamos um valor padrão.
      [self sendEventWithName:@"IbeaconLocalAddress" body:@{@"localAddress": @"N/A"}];
    }
  }
}

- (void)stopScanning {
  if (self.centralManager) {
    [self.centralManager stopScan];
  }
}

#pragma mark - CBCentralManagerDelegate

- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
  if (central.state == CBManagerStatePoweredOn) {
    // Inicia o scan assim que o Bluetooth estiver ativo
    [central scanForPeripheralsWithServices:nil options:@{ CBCentralManagerScanOptionAllowDuplicatesKey : @YES }];
    if (self.hasListeners) {
      [self sendEventWithName:@"IbeaconLocalAddress" body:@{@"localAddress": @"N/A"}];
    }
  } else {
    NSString *errorMsg = @"Bluetooth não está disponível";
    [self sendEventWithName:@"ScanError" body:@{@"message": errorMsg, @"type": @"iBeacon"}];
  }
}

- (void)centralManager:(CBCentralManager *)central
 didDiscoverPeripheral:(CBPeripheral *)peripheral
     advertisementData:(NSDictionary<NSString *,id> *)advertisementData
                  RSSI:(NSNumber *)RSSI {
  NSData *manufacturerData = advertisementData[CBAdvertisementDataManufacturerDataKey];
  if (manufacturerData && manufacturerData.length >= 25) {
    // Verifica se os dois primeiros bytes são 0x004C (Apple)
    const uint8_t *bytes = manufacturerData.bytes;
    uint16_t companyID = (bytes[0] << 8) | bytes[1];
    if (companyID == 0x004C) {
      // Verifica o tipo (deve ser 0x02) e o tamanho (0x15)
      uint8_t beaconType = bytes[2];
      uint8_t beaconLength = bytes[3];
      if (beaconType == 0x02 && beaconLength == 0x15) {
        // Extrai UUID, major, minor, txPower
        NSData *uuidData = [manufacturerData subdataWithRange:NSMakeRange(4, 16)];
        NSMutableString *uuidString = [NSMutableString string];
        const unsigned char *uuidBytes = uuidData.bytes;
        for (int i = 0; i < uuidData.length; i++) {
          [uuidString appendFormat:@"%02X", uuidBytes[i]];
        }
        uint16_t major = (bytes[20] << 8) | bytes[21];
        uint16_t minor = (bytes[22] << 8) | bytes[23];
        int8_t txPower = bytes[24];
        double rssi = [RSSI doubleValue];
        // Calcula a distância usando a mesma fórmula: distance = 10^((txPower - rssi) / (10 * n)), com n=2.
        double n = 2.0;
        double distanceCm = pow(10, ((txPower - rssi) / (10 * n))) * 100.0;

        NSMutableDictionary *result = [NSMutableDictionary dictionary];
        result[@"id"] = uuidString;
        result[@"major"] = @(major);
        result[@"minor"] = @(minor);
        result[@"txPower"] = @(txPower);
        result[@"rssi"] = RSSI;
        result[@"distance"] = @(distanceCm);
        result[@"type"] = @"iBeacon";

        if (self.hasListeners) {
          [self sendEventWithName:@"ScanResult" body:result];
        }
      }
    }
  }
}

RCT_EXPORT_METHOD(startScan:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
  [self startScanning];
  resolve(@"Scan iniciado");
}

RCT_EXPORT_METHOD(stopScan:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
  [self stopScanning];
  resolve(@(YES));
}

RCT_EXPORT_METHOD(checkPermissions:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
  resolve(@(YES));
}

RCT_EXPORT_METHOD(isBleSupported:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
  // Supondo que se o CoreBluetooth inicializou, BLE é suportado.
  resolve(@(YES));
}

@end
