# By: Riasat Ullah
# This class represents a Service object.

from utils import times, var_names
import datetime
import uuid


class Service(object):

    def __init__(self, service_id, organization_id, service_name, description, is_enabled, for_policy_id,
                 re_trigger_minutes=None, support_days=None, support_start=None, support_end=None,
                 support_timezone=None, off_hours_deprioritize=None, on_hours_reprioritize=None, allow_grouping=None,
                 service_ref_id=None, current_maintenances=None, service_integrations=None, business_services=None):
        self.service_id = service_id
        self.organization_id = organization_id
        self.service_name = service_name
        self.description = description
        self.is_enabled = is_enabled
        self.for_policy_id = for_policy_id
        self.re_trigger_minutes = re_trigger_minutes
        self.support_days = support_days
        self.support_start = support_start
        self.support_end = support_end
        self.support_timezone = support_timezone
        self.off_hours_deprioritize = off_hours_deprioritize
        self.on_hours_reprioritize = on_hours_reprioritize
        self.allow_grouping = allow_grouping
        self.service_ref_id = service_ref_id
        self.current_maintenances = current_maintenances
        self.service_integrations = service_integrations
        self.business_services = business_services

    @staticmethod
    def create_service(details):
        '''
        Creates a Service object from details of a service.
        :param details: (dict) of service details
        :return: Service object
        '''
        srv_ref = details[var_names.service_ref_id] if var_names.service_ref_id in details else None
        if srv_ref is not None and isinstance(srv_ref, str):
            srv_ref = uuid.UUID(srv_ref)

        return Service(
            details[var_names.service_id], details[var_names.organization_id],
            details[var_names.service_name], details[var_names.description],
            details[var_names.is_enabled], details[var_names.for_policyid],
            re_trigger_minutes=details[var_names.re_trigger_minutes] if var_names.re_trigger_minutes in details
            else None,
            support_days=details[var_names.support_days]
            if var_names.support_days in details and details[var_names.support_days] is not None else None,
            support_start=times.get_time_from_string(details[var_names.support_start])
            if var_names.support_start in details and details[var_names.support_start] is not None else None,
            support_end=times.get_time_from_string(details[var_names.support_end])
            if var_names.support_end in details and details[var_names.support_end] is not None else None,
            support_timezone=details[var_names.timezone] if var_names.timezone in details else None,
            off_hours_deprioritize=details[var_names.de_prioritize] if var_names.de_prioritize in details else None,
            on_hours_reprioritize=details[var_names.re_prioritize] if var_names.re_prioritize in details else None,
            allow_grouping=details[var_names.allow_grouping] if var_names.allow_grouping in details else None,
            service_ref_id=srv_ref,
            current_maintenances=details[var_names.maintenances] if var_names.maintenances in details else None,
            service_integrations=details[var_names.integrations] if var_names.integrations in details else None,
            business_services=details[var_names.business_services] if var_names.business_services in details else None
        )

    def is_available(self, timestamp):
        '''
        Checks if a service is available to accept incidents or not at a given timestamp.
        :param timestamp: UTC timestamp to check on
        :return: (boolean) True if service is available; False otherwise
        '''
        if self.is_enabled and not self.in_maintenance(timestamp):
            return True
        return False

    def in_maintenance(self, timestamp):
        '''
        Checks if a service is in maintenance or not at a given timestamp.
        :param timestamp: UTC timestamp to check on
        :return: (boolean) True if service is in maintenance; False otherwise
        '''
        if self.current_maintenances is None:
            return False
        else:
            for item in self.current_maintenances:
                timezone_timestamp = times.utc_to_region_time(timestamp, item[2])
                if item[0] <= timezone_timestamp < item[1]:
                    return True
            return False

    def in_support_mode(self, timestamp):
        '''
        Checks if a service is in support mode at a given timestamp.
        :param timestamp: UTC timestamp to check on
        :return: (boolean) True if service is in support mode; False otherwise
        '''
        if self.support_days is None:
            return True
        else:
            timezone_timestamp = times.utc_to_region_time(timestamp, self.support_timezone)
            if timezone_timestamp.weekday() in self.support_days and\
                    self.support_start <= timezone_timestamp.time() < self.support_end:
                return True
            else:
                return False

    def next_utc_support_start(self, timestamp):
        '''
        Gets the next UTC time this service's support hours will start.
        :param timestamp: UTC timestamp to check on
        :return: (datetime.datetime) UTC timestamp
        '''
        if self.in_support_mode(timestamp):
            return timestamp
        else:
            local_timestamp = times.utc_to_region_time(timestamp, self.support_timezone)
            localized_start = datetime.datetime.combine(local_timestamp.date(), self.support_start)

            if (self.support_days is None or local_timestamp.weekday() in self.support_days)\
                    and local_timestamp <= localized_start:
                return times.region_to_utc_time(localized_start, self.support_timezone)
            else:
                for i in range(1, 8):
                    check_timestamp = localized_start + datetime.timedelta(days=i)
                    if (self.support_days is None or check_timestamp.weekday() in self.support_days) and \
                            check_timestamp.time() <= self.support_start:
                        localized_start = check_timestamp
                        break
            return times.region_to_utc_time(localized_start, self.support_timezone)

    def to_dict(self):
        '''
        Gets the details of the Service as a dict.
        :return: (dict) of Service details
        '''
        data = {
            var_names.service_id: self.service_id,
            var_names.organization_id: self.organization_id,
            var_names.service_name: self.service_name,
            var_names.description: self.description,
            var_names.is_enabled: self.is_enabled,
            var_names.for_policyid: self.for_policy_id,
            var_names.re_trigger_minutes: self.re_trigger_minutes,
            var_names.support_days: self.support_days,
            var_names.support_start: self.support_start,
            var_names.support_end: self.support_end,
            var_names.timezone: self.support_timezone,
            var_names.de_prioritize: self.off_hours_deprioritize,
            var_names.re_prioritize: self.on_hours_reprioritize,
            var_names.allow_grouping: self.allow_grouping,
            var_names.service_ref_id: self.service_ref_id,
            var_names.maintenances: self.current_maintenances,
            var_names.integrations: self.service_integrations,
            var_names.business_services: self.business_services
        }
        return data
