"""
  Copyright (c) 2016- 2025, Wiliot Ltd. All rights reserved.

  Redistribution and use of the Software in source and binary forms, with or without modification,
   are permitted provided that the following conditions are met:

     1. Redistributions of source code must retain the above copyright notice,
     this list of conditions and the following disclaimer.

     2. Redistributions in binary form, except as used in conjunction with
     Wiliot's Pixel in a product or a Software update for such product, must reproduce
     the above copyright notice, this list of conditions and the following disclaimer in
     the documentation and/or other materials provided with the distribution.

     3. Neither the name nor logo of Wiliot, nor the names of the Software's contributors,
     may be used to endorse or promote products or services derived from this Software,
     without specific prior written permission.

     4. This Software, with or without modification, must only be used in conjunction
     with Wiliot's Pixel or with Wiliot's cloud service.

     5. If any Software is provided in binary form under this license, you must not
     do any of the following:
     (a) modify, adapt, translate, or create a derivative work of the Software; or
     (b) reverse engineer, decompile, disassemble, decrypt, or otherwise attempt to
     discover the source code or non-literal aspects (such as the underlying structure,
     sequence, organization, ideas, or algorithms) of the Software.

     6. If you create a derivative work and/or improvement of any Software, you hereby
     irrevocably grant each of Wiliot and its corporate affiliates a worldwide, non-exclusive,
     royalty-free, fully paid-up, perpetual, irrevocable, assignable, sublicensable
     right and license to reproduce, use, make, have made, import, distribute, sell,
     offer for sale, create derivative works of, modify, translate, publicly perform
     and display, and otherwise commercially exploit such derivative works and improvements
     (as applicable) in conjunction with Wiliot's products and services.

     7. You represent and warrant that you are not a resident of (and will not use the
     Software in) a country that the U.S. government has embargoed for use of the Software,
     nor are you named on the U.S. Treasury Department's list of Specially Designated
     Nationals or any other applicable trade sanctioning regulations of any jurisdiction.
     You must not transfer, export, re-export, import, re-import or divert the Software
     in violation of any export or re-export control laws and regulations (such as the
     United States' ITAR, EAR, and OFAC regulations), as well as any applicable import
     and use restrictions, all as then in effect

   THIS SOFTWARE IS PROVIDED BY WILIOT "AS IS" AND "AS AVAILABLE", AND ANY EXPRESS
   OR IMPLIED WARRANTIES OR CONDITIONS, INCLUDING, BUT NOT LIMITED TO, ANY IMPLIED
   WARRANTIES OR CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, NONINFRINGEMENT,
   QUIET POSSESSION, FITNESS FOR A PARTICULAR PURPOSE, AND TITLE, ARE DISCLAIMED.
   IN NO EVENT SHALL WILIOT, ANY OF ITS CORPORATE AFFILIATES OR LICENSORS, AND/OR
   ANY CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
   OR CONSEQUENTIAL DAMAGES, FOR THE COST OF PROCURING SUBSTITUTE GOODS OR SERVICES,
   FOR ANY LOSS OF USE OR DATA OR BUSINESS INTERRUPTION, AND/OR FOR ANY ECONOMIC LOSS
   (SUCH AS LOST PROFITS, REVENUE, ANTICIPATED SAVINGS). THE FOREGOING SHALL APPLY:
   (A) HOWEVER CAUSED AND REGARDLESS OF THE THEORY OR BASIS LIABILITY, WHETHER IN
   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE);
   (B) EVEN IF ANYONE IS ADVISED OF THE POSSIBILITY OF ANY DAMAGES, LOSSES, OR COSTS; AND
   (C) EVEN IF ANY REMEDY FAILS OF ITS ESSENTIAL PURPOSE.
"""
from enum import Enum
from wiliot_api.api_client import Client, WiliotCloudError


class EventNotFound(Exception):
    pass


class EventConfigurationNotFound(Exception):
    pass


class ProcessorTemplateNotFound(Exception):
    pass


class EventType(Enum):
    """Event type UUIDs for configurable sensing events"""
    LOCATION = 'c63ac9ae-eb34-476f-8bd5-02f9f60da0c4'
    GEOLOCATION = '48ebc272-ec2d-4eb0-8285-29c21335b1b1'
    TEMPERATURE = 'f7c73a2b-fcda-4782-a195-6426a72b2688'
    GEOFENCING = '9c75b584-9a3d-4269-84af-0f942fbe9419'


class EventTrigger(Enum):
    """Event trigger types"""
    ON_CHANGE = 'on_change'
    PERIODIC = 'periodic'
    ON_INACTIVITY = 'on_inactivity'
    ON_INFERENCE = 'on_inference'
    ON_ENTRY = 'on_entry'
    ON_EXIT = 'on_exit'


class ConfidenceThreshold(Enum):
    """Confidence threshold options for location events"""
    ALL = 'all'
    PERCENTAGE_75 = 'percentage_75'
    PERCENTAGE_50 = 'percentage_50'


class ProcessorId(Enum):
    """Processor IDs for different event types"""
    LOCATION_ISOLATED_ZONES = '159b3b16-e00c-4b10-9112-a09403613a22'
    LOCATION_OVERLAPPING_ZONES = '05d29340-4019-438e-8bea-c77514e45b01'
    GEOLOCATION = '1371744a-18e4-4edd-b501-f833bdd47ab8'
    TEMPERATURE = 'a31e7aff-7302-4c2e-8a6c-e66edebb5a23'
    GEOFENCING = 'd981ebf7-f784-4be1-bfb7-888b9411b515'


class ProcessorTemplateId(Enum):
    """Processor template IDs for location events"""
    CUSTOM_TEMPLATE = 'aa52496f-2da0-42a1-9a4a-50af995a7f6f'
    NO_ZONE_BLEED = '681c0253-9db6-4d49-b34c-1e438bff922f'
    SHORT_DWELL_WITH_ZONE_BLEED = 'f939fab2-4502-4a23-b45e-56275f53b34f'
    LONG_PACING_INTERVAL_WITH_ZONE_BLEED = '1c64ff99-d649-42c4-8c44-80f749e5816e'


class ConfigurableSensingEventsClient(Client):
    def __init__(self, api_key, owner_id, env='prod', region='us-east-2', cloud='', log_file=None, logger_=None,
                 keep_alive=True, initiator_name=None, base_url=None):
        self.client_path = f"v1/owner/{owner_id}/event-registry/"
        self.owner_id = owner_id
        super().__init__(api_key=api_key, env=env, region=region, cloud=cloud, log_file=log_file, logger_=logger_,
                         keep_alive=keep_alive, initiator_name=initiator_name, base_url=base_url)

    def _get_base_url(self, override_client_path=None, override_api_version=None):
        """Override base URL construction for this API which uses a different path structure"""
        # base_path already ends with '/', so don't add another one
        base = self.base_path.rstrip('/')
        if override_client_path is not None:
            return f"{base}/{override_client_path}"
        return f"{base}/{self.client_path}"

    # Create Events

    def create_event(self, event_name, event_type, asset_category_ids, is_active=True,
                     description='', zone_labels=None, asset_labels=None, location_labels=None,
                     event_trigger=None, event_trigger_cadence=None, event_trigger_visibility_timeout=None,
                     confidence_threshold=None, event_retention_period=None, geofence_range=None,
                     geolocation_distance_threshold=None, c2c_connection_ids=None,
                     processor_id=None, processor_template_id=None, processor_configuration=None,
                     include_temperature=None, tag_type=None):
        """
        Create a new configurable sensing event
        :param event_name: String - Required - User-defined event name. Must start with a lowercase letter,
                          followed by letters and numbers. Spaces and special characters are not allowed.
                          Maximum length: 30 characters.
        :param event_type: EventType or String - Required - The type of event (LOCATION, GEOLOCATION, TEMPERATURE, GEOFENCING)
        :param asset_category_ids: List - Required - List of asset category IDs for which the event should be triggered
        :param is_active: Boolean - Optional - Whether the event is active (default: True)
        :param description: String - Optional - User-defined event description. Maximum length: 100 characters.
        :param zone_labels: List - Optional - List of dictionaries with 'id' and 'value' keys for zone labels
        :param asset_labels: List - Optional - List of dictionaries with 'id' and 'value' keys for asset labels
        :param location_labels: List - Optional - List of dictionaries with 'id' and 'value' keys for location labels
        :param event_trigger: EventTrigger or String - Optional - The trigger condition for the event
        :param event_trigger_cadence: Integer - Optional - How often (in minutes) the event will be triggered (1-1440)
        :param event_trigger_visibility_timeout: Integer - Optional - Minutes after which asset is considered not visible (1-1440)
        :param confidence_threshold: ConfidenceThreshold or String - Optional - Confidence threshold for location events
        :param event_retention_period: Integer - Optional - Minutes to retain the asset's location (1-1440)
        :param geofence_range: Integer - Optional - Geofence radius in meters (20-10000)
        :param geolocation_distance_threshold: Integer - Optional - Minimum distance in meters to trigger geolocation event
        :param c2c_connection_ids: List - Optional - List of C2C connection IDs for event notifications
        :param processor_id: ProcessorId or String - Required for Location events, optional for others - Processor ID for the event
        :param processor_template_id: ProcessorTemplateId or String - Optional - Processor template ID
        :param processor_configuration: String - Optional - Custom processor configuration JSON
        :param include_temperature: Boolean - Optional - Include temperature data in Location events.
                                   Only applicable for Location events with OverlappingZones processor.
        :param tag_type: String - Optional - Tag type identifier. Required for Light and Humidity events.
        :return: The created event if successful
        """
        path = "events/new"
        payload = {
            "eventName": event_name,
            "eventType": event_type.value if isinstance(event_type, EventType) else event_type,
            "assetCategoryIds": asset_category_ids,
            "isActive": is_active,
            "description": description if description is not None else "",
            "c2cConnectionIds": c2c_connection_ids if c2c_connection_ids is not None else []
        }
        if zone_labels is not None:
            payload["zoneLabels"] = zone_labels
        if asset_labels is not None:
            payload["assetLabels"] = asset_labels
        if location_labels is not None:
            payload["locationLabels"] = location_labels
        if event_trigger is not None:
            payload["eventTrigger"] = event_trigger.value if isinstance(event_trigger, EventTrigger) else event_trigger
        if event_trigger_cadence is not None:
            payload["eventTriggerCadence"] = event_trigger_cadence
        if event_trigger_visibility_timeout is not None:
            payload["eventTriggerVisibilityTimeout"] = event_trigger_visibility_timeout
        if confidence_threshold is not None:
            payload["confidenceThreshold"] = confidence_threshold.value if isinstance(confidence_threshold, ConfidenceThreshold) else confidence_threshold
        if event_retention_period is not None:
            payload["eventRetentionPeriod"] = event_retention_period
        if geofence_range is not None:
            payload["geofenceRange"] = geofence_range
        if geolocation_distance_threshold is not None:
            payload["geolocationDistanceThreshold"] = geolocation_distance_threshold
        if processor_id is not None:
            payload["processorId"] = processor_id.value if isinstance(processor_id, ProcessorId) else processor_id
        if processor_template_id is not None:
            payload["processorTemplateId"] = processor_template_id.value if isinstance(processor_template_id, ProcessorTemplateId) else processor_template_id
        if processor_configuration is not None:
            payload["processorConfiguration"] = processor_configuration
        if include_temperature is not None:
            payload["includeTemperature"] = include_temperature
        if tag_type is not None:
            payload["tagType"] = tag_type

        try:
            res = self._post(path, payload)
            return res.get('data', res)
        except WiliotCloudError as e:
            print("Failed to create event")
            raise e

    def duplicate_event(self, event_id):
        """
        Duplicate an existing event
        :param event_id: String - Required - The ID of the event to duplicate
        :return: The duplicated event if successful
        """
        path = f"events/{event_id}/duplicate"
        try:
            res = self._post(path, payload={})
            return res.get('data', res)
        except WiliotCloudError as e:
            print("Failed to duplicate event")
            raise e

    # Manage Events

    def update_event(self, event_id, event_name, event_type, asset_category_ids, is_active,
                     description=None, zone_labels=None, asset_labels=None, location_labels=None,
                     event_trigger=None, event_trigger_cadence=None, event_trigger_visibility_timeout=None,
                     confidence_threshold=None, event_retention_period=None, geofence_range=None,
                     geolocation_distance_threshold=None, c2c_connection_ids=None,
                     processor_id=None, processor_template_id=None, processor_configuration=None,
                     include_temperature=None, tag_type=None):
        """
        Update an existing event (full update)
        :param event_id: String - Required - The ID of the event to update
        :param event_name: String - Required - User-defined event name
        :param event_type: EventType or String - Required - The type of event
        :param asset_category_ids: List - Required - List of asset category IDs
        :param is_active: Boolean - Required - Whether the event is active
        :param description: String - Optional - User-defined event description
        :param zone_labels: List - Optional - List of zone labels
        :param asset_labels: List - Optional - List of asset labels
        :param location_labels: List - Optional - List of location labels
        :param event_trigger: EventTrigger or String - Optional - The trigger condition
        :param event_trigger_cadence: Integer - Optional - Trigger cadence in minutes (1-1440)
        :param event_trigger_visibility_timeout: Integer - Optional - Visibility timeout in minutes (1-1440)
        :param confidence_threshold: ConfidenceThreshold or String - Optional - Confidence threshold
        :param event_retention_period: Integer - Optional - Retention period in minutes (1-1440)
        :param geofence_range: Integer - Optional - Geofence radius in meters (20-10000)
        :param geolocation_distance_threshold: Integer - Optional - Distance threshold in meters
        :param c2c_connection_ids: List - Optional - List of C2C connection IDs
        :param processor_id: ProcessorId or String - Optional - Processor ID
        :param processor_template_id: ProcessorTemplateId or String - Optional - Processor template ID
        :param processor_configuration: String - Optional - Custom processor configuration
        :param include_temperature: Boolean - Optional - Include temperature data in Location events.
                                   Only applicable for Location events with OverlappingZones processor.
        :param tag_type: String - Optional - Tag type identifier. Required for Light and Humidity events.
        :return: The updated event if successful
        """
        path = f"events/{event_id}"
        payload = {
            "id": event_id,
            "eventName": event_name,
            "eventType": event_type.value if isinstance(event_type, EventType) else event_type,
            "assetCategoryIds": asset_category_ids,
            "isActive": is_active
        }

        if description is not None:
            payload["description"] = description
        if zone_labels is not None:
            payload["zoneLabels"] = zone_labels
        if asset_labels is not None:
            payload["assetLabels"] = asset_labels
        if location_labels is not None:
            payload["locationLabels"] = location_labels
        if event_trigger is not None:
            payload["eventTrigger"] = event_trigger.value if isinstance(event_trigger, EventTrigger) else event_trigger
        if event_trigger_cadence is not None:
            payload["eventTriggerCadence"] = event_trigger_cadence
        if event_trigger_visibility_timeout is not None:
            payload["eventTriggerVisibilityTimeout"] = event_trigger_visibility_timeout
        if confidence_threshold is not None:
            payload["confidenceThreshold"] = confidence_threshold.value if isinstance(confidence_threshold, ConfidenceThreshold) else confidence_threshold
        if event_retention_period is not None:
            payload["eventRetentionPeriod"] = event_retention_period
        if geofence_range is not None:
            payload["geofenceRange"] = geofence_range
        if geolocation_distance_threshold is not None:
            payload["geolocationDistanceThreshold"] = geolocation_distance_threshold
        if c2c_connection_ids is not None:
            payload["c2cConnectionIds"] = c2c_connection_ids
        if processor_id is not None:
            payload["processorId"] = processor_id.value if isinstance(processor_id, ProcessorId) else processor_id
        if processor_template_id is not None:
            payload["processorTemplateId"] = processor_template_id.value if isinstance(processor_template_id, ProcessorTemplateId) else processor_template_id
        if processor_configuration is not None:
            payload["processorConfiguration"] = processor_configuration
        if include_temperature is not None:
            payload["includeTemperature"] = include_temperature
        if tag_type is not None:
            payload["tagType"] = tag_type

        try:
            res = self._put(path, payload)
            return res.get('data', res)
        except WiliotCloudError as e:
            print("Failed to update event")
            raise e

    def activate_event(self, event_id, is_active):
        """
        Activate or deactivate an event
        :param event_id: String - Required - The ID of the event to activate/deactivate
        :param is_active: Boolean - Required - True to activate, False to deactivate
        :return: The updated event if successful
        """
        path = f"events/{event_id}"
        payload = {
            "isActive": is_active
        }
        try:
            res = self._patch(path, payload)
            return res.get('data', res)
        except WiliotCloudError as e:
            print("Failed to activate/deactivate event")
            raise e

    def deactivate_event(self, event_id):
        """
        Deactivate an event (convenience method)
        :param event_id: String - Required - The ID of the event to deactivate
        :return: The updated event if successful
        """
        return self.activate_event(event_id, is_active=False)

    def delete_event(self, event_id):
        """
        Delete an event. Caution: this action cannot be reversed.
        :param event_id: String - Required - The ID of the event to delete
        :return: True if the event was deleted
        """
        path = f"events/{event_id}"
        try:
            res = self._delete(path)
            return res.get('message', '').lower().find("success") != -1 or res.get('status_code') == 200
        except WiliotCloudError as e:
            print("Failed to delete event")
            raise e

    # Event Data

    def get_events(self):
        """
        Get list of all events for the owner
        :return: A list of event dictionaries
        """
        path = "events"
        try:
            res = self._get(path)
            return res.get('data', [])
        except WiliotCloudError as e:
            print("Failed to get events")
            raise e

    def get_event(self, event_id):
        """
        Get event configuration for a specific event ID
        :param event_id: String - Required - The event ID to retrieve
        :return: A dictionary with event configuration
        :raises: EventNotFound if the event does not exist
        """
        path = f"events/{event_id}"
        try:
            res = self._get(path)
            data = res.get('data', None)
            if data is None or len(data) == 0:
                raise EventNotFound
            return data
        except WiliotCloudError as e:
            if e.args[0].get('status_code') == 404:
                raise EventNotFound
            print("Failed to get event")
            raise e

    def get_event_metrics(self, event_config_id, duration, step=10):
        """
        Get Prometheus metrics for a specific event configuration
        :param event_config_id: String - Required - The event configuration ID
        :param duration: Integer - Required - Duration in seconds to subtract from current time (60-21600)
        :param step: Integer - Optional - Seconds between samples (1-15, default: 10)
        :return: A dictionary with metric values
        """
        path = f"events/{event_config_id}/metrics"
        params = {
            'duration': duration,
            'step': step
        }
        try:
            res = self._get(path, params=params)
            return res.get('data', {})
        except WiliotCloudError as e:
            print("Failed to get event metrics")
            raise e

    # Configuration

    def get_processor_template(self, template_id):
        """
        Get processor configuration for a specific template ID
        :param template_id: ProcessorTemplateId or String - Required - The template ID to retrieve
                           Available templates:
                           - CUSTOM_TEMPLATE: aa52496f-2da0-42a1-9a4a-50af995a7f6f
                           - NO_ZONE_BLEED: 681c0253-9db6-4d49-b34c-1e438bff922f
                           - SHORT_DWELL_WITH_ZONE_BLEED: f939fab2-4502-4a23-b45e-56275f53b34f
                           - LONG_PACING_INTERVAL_WITH_ZONE_BLEED: 1c64ff99-d649-42c4-8c44-80f749e5816e
        :return: A dictionary with processor template configuration
        :raises: ProcessorTemplateNotFound if the template does not exist
        """
        template_id_value = template_id.value if isinstance(template_id, ProcessorTemplateId) else template_id
        path = f"events/processorTemplate/{template_id_value}"
        try:
            res = self._get(path)
            data = res.get('data', None)
            if data is None:
                raise ProcessorTemplateNotFound
            return data
        except WiliotCloudError as e:
            if e.args[0].get('status_code') == 404:
                raise ProcessorTemplateNotFound
            print("Failed to get processor template")
            raise e

    # Helper methods for creating specific event types

    def create_location_event(self, event_name, asset_category_ids, event_trigger,
                              is_active=True, description=None, zone_labels=None, asset_labels=None,
                              confidence_threshold=ConfidenceThreshold.ALL, event_retention_period=None,
                              event_trigger_cadence=None, event_trigger_visibility_timeout=None,
                              c2c_connection_ids=None, processor_id=ProcessorId.LOCATION_ISOLATED_ZONES,
                              processor_template_id=None, processor_configuration=None,
                              include_temperature=None):
        """
        Create a location event (convenience method)
        :param event_name: String - Required - Event name
        :param asset_category_ids: List - Required - List of asset category IDs
        :param event_trigger: EventTrigger - Required - One of ON_CHANGE, PERIODIC, ON_INACTIVITY, ON_INFERENCE
        :param is_active: Boolean - Optional - Whether the event is active (default: True)
        :param description: String - Optional - Event description
        :param zone_labels: List - Optional - Zone labels for event generation
        :param asset_labels: List - Optional - Asset labels to filter events
        :param confidence_threshold: ConfidenceThreshold - Required - Confidence threshold (default: ALL).
                                   Options: ALL, PERCENTAGE_75, PERCENTAGE_50
        :param event_retention_period: Integer - Optional - Retention period in minutes (1-1440)
        :param event_trigger_cadence: Integer - Optional - Trigger cadence for periodic events (1-1440)
        :param event_trigger_visibility_timeout: Integer - Optional - Visibility timeout for inactivity events (1-1440)
        :param c2c_connection_ids: List - Optional - C2C connection IDs for notifications
        :param processor_id: ProcessorId - Required - Processor ID (default: LOCATION_ISOLATED_ZONES).
                            Use LOCATION_OVERLAPPING_ZONES for zones with signal bleed.
        :param processor_template_id: ProcessorTemplateId - Optional - Processor template ID
        :param processor_configuration: String - Optional - Custom processor configuration
        :param include_temperature: Boolean - Optional - Include temperature data. Only applicable with OverlappingZones processor.
        :return: The created event
        """
        return self.create_event(
            event_name=event_name,
            event_type=EventType.LOCATION,
            asset_category_ids=asset_category_ids,
            is_active=is_active,
            description=description,
            zone_labels=zone_labels,
            asset_labels=asset_labels,
            event_trigger=event_trigger,
            event_trigger_cadence=event_trigger_cadence,
            event_trigger_visibility_timeout=event_trigger_visibility_timeout,
            confidence_threshold=confidence_threshold,
            event_retention_period=event_retention_period,
            c2c_connection_ids=c2c_connection_ids,
            processor_id=processor_id,
            processor_template_id=processor_template_id,
            processor_configuration=processor_configuration,
            include_temperature=include_temperature
        )

    def create_geolocation_event(self, event_name, asset_category_ids, event_trigger,
                                 is_active=True, description=None, asset_labels=None,
                                 geolocation_distance_threshold=None, event_trigger_cadence=None,
                                 c2c_connection_ids=None, processor_id=ProcessorId.GEOLOCATION):
        """
        Create a geolocation event (convenience method)
        :param event_name: String - Required - Event name
        :param asset_category_ids: List - Required - List of asset category IDs
        :param event_trigger: EventTrigger - Required - One of ON_CHANGE or PERIODIC
        :param is_active: Boolean - Optional - Whether the event is active (default: True)
        :param description: String - Optional - Event description
        :param asset_labels: List - Optional - Asset labels to filter events
        :param geolocation_distance_threshold: Integer - Optional - Minimum distance in meters to trigger event
        :param event_trigger_cadence: Integer - Optional - Trigger cadence for periodic events (1-1440)
        :param c2c_connection_ids: List - Optional - C2C connection IDs for notifications
        :param processor_id: ProcessorId - Optional - Processor ID (default: GEOLOCATION)
        :return: The created event
        """
        return self.create_event(
            event_name=event_name,
            event_type=EventType.GEOLOCATION,
            asset_category_ids=asset_category_ids,
            is_active=is_active,
            description=description,
            asset_labels=asset_labels,
            event_trigger=event_trigger,
            event_trigger_cadence=event_trigger_cadence,
            geolocation_distance_threshold=geolocation_distance_threshold,
            c2c_connection_ids=c2c_connection_ids,
            processor_id=processor_id
        )

    def create_temperature_event(self, event_name, asset_category_ids, event_trigger,
                                 is_active=True, description=None, asset_labels=None,
                                 event_trigger_cadence=None, c2c_connection_ids=None,
                                 processor_id=ProcessorId.TEMPERATURE):
        """
        Create a temperature event (convenience method)
        :param event_name: String - Required - Event name
        :param asset_category_ids: List - Required - List of asset category IDs
        :param event_trigger: EventTrigger - Required - One of PERIODIC or ON_INFERENCE
        :param is_active: Boolean - Optional - Whether the event is active (default: True)
        :param description: String - Optional - Event description
        :param asset_labels: List - Optional - Asset labels to filter events
        :param event_trigger_cadence: Integer - Optional - Trigger cadence for periodic events (1-1440)
        :param c2c_connection_ids: List - Optional - C2C connection IDs for notifications
        :param processor_id: ProcessorId - Optional - Processor ID (default: TEMPERATURE)
        :return: The created event
        """
        return self.create_event(
            event_name=event_name,
            event_type=EventType.TEMPERATURE,
            asset_category_ids=asset_category_ids,
            is_active=is_active,
            description=description,
            asset_labels=asset_labels,
            event_trigger=event_trigger,
            event_trigger_cadence=event_trigger_cadence,
            c2c_connection_ids=c2c_connection_ids,
            processor_id=processor_id
        )

    def create_geofencing_event(self, event_name, asset_category_ids, event_trigger, geofence_range,
                                location_labels, is_active=True, description=None, asset_labels=None,
                                c2c_connection_ids=None, processor_id=ProcessorId.GEOFENCING):
        """
        Create a geofencing event (convenience method)
        :param event_name: String - Required - Event name
        :param asset_category_ids: List - Required - List of asset category IDs
        :param event_trigger: EventTrigger - Required - One of ON_ENTRY or ON_EXIT
        :param geofence_range: Integer - Required - Geofence radius in meters (20-10000)
        :param location_labels: List - Required - Location labels for geofence sites
        :param is_active: Boolean - Optional - Whether the event is active (default: True)
        :param description: String - Optional - Event description
        :param asset_labels: List - Optional - Asset labels to filter events
        :param c2c_connection_ids: List - Optional - C2C connection IDs for notifications
        :param processor_id: ProcessorId - Optional - Processor ID (default: GEOFENCING)
        :return: The created event
        """
        return self.create_event(
            event_name=event_name,
            event_type=EventType.GEOFENCING,
            asset_category_ids=asset_category_ids,
            is_active=is_active,
            description=description,
            asset_labels=asset_labels,
            location_labels=location_labels,
            event_trigger=event_trigger,
            geofence_range=geofence_range,
            c2c_connection_ids=c2c_connection_ids,
            processor_id=processor_id
        )