class GeofenceConfig {
  GeofenceConfig({this.lat, this.lng, this.radiusMeters, this.polygon});

  final double? lat;
  final double? lng;
  final double? radiusMeters;
  final List<GeofencePoint>? polygon;

  bool get hasPolygon => polygon?.isNotEmpty == true;
  bool get hasCircle =>
      lat != null && lng != null && radiusMeters != null && radiusMeters! > 0;

  bool containsPolygon(double pointLat, double pointLng) {
    final points = polygon;
    if (points == null || points.isEmpty) return false;
    var inside = false;
    for (var i = 0, j = points.length - 1; i < points.length; j = i++) {
      final xi = points[i].lng;
      final yi = points[i].lat;
      final xj = points[j].lng;
      final yj = points[j].lat;

      final intersects =
          ((yi > pointLat) != (yj > pointLat)) &&
          (pointLng < (xj - xi) * (pointLat - yi) / (yj - yi) + xi);
      if (intersects) {
        inside = !inside;
      }
    }
    return inside;
  }

  factory GeofenceConfig.fromJson(Map<String, dynamic> json) {
    final rawPolygon = json['polygon'] ?? json['points'];
    final polygonPoints = <GeofencePoint>[];
    if (rawPolygon is List) {
      for (final entry in rawPolygon) {
        if (entry is Map<String, dynamic>) {
          polygonPoints.add(GeofencePoint.fromJson(entry));
        } else if (entry is Map) {
          polygonPoints.add(
            GeofencePoint.fromJson(Map<String, dynamic>.from(entry)),
          );
        }
      }
    }

    return GeofenceConfig(
      lat: (json['lat'] as num?)?.toDouble(),
      lng: (json['lng'] as num?)?.toDouble(),
      radiusMeters: (json['radius_m'] as num?)?.toDouble(),
      polygon: polygonPoints.isEmpty ? null : polygonPoints,
    );
  }
}

class GeofencePoint {
  const GeofencePoint({required this.lat, required this.lng});

  final double lat;
  final double lng;

  factory GeofencePoint.fromJson(Map<String, dynamic> json) {
    return GeofencePoint(
      lat: (json['lat'] as num).toDouble(),
      lng: (json['lng'] as num).toDouble(),
    );
  }
}

class AppSetting {
  AppSetting({required this.key, required this.value});

  final String key;
  final Map<String, dynamic> value;

  factory AppSetting.fromMap(Map<String, dynamic> map) {
    return AppSetting(
      key: map['key'] as String,
      value: Map<String, dynamic>.from(map['value'] as Map),
    );
  }
}
