import re

import pendulum


class NumListHelper(object):
    @classmethod
    def parse_num_list(cls, string):
        """ parse num list like: '1-6' or '2'.
        # NOTE: DO NOT use string like '1 -6' or '1 - 6', it will raise
        an exception.
        """
        m = re.match(r'(\d+)(?:-(\d+))?$', string)
        start = m.group(1)
        end = m.group(2) or start
        return list(range(int(start, 10), int(end, 10) + 1))

    @classmethod
    def parse_comma_delimited_num(cls, string):
        """ parse comma delimited num list like: '1,3-8,10-15'.
        # NOTE: the return list is NOT sorted.
        """
        num_str_l = [i.strip() for i in string.split(',')]
        num_str_l = filter(lambda x: x != '', num_str_l)
        n_list = []  # final format like: [1,2,3,4,9,10,11,12]
        for n_str in num_str_l:
            n_list.extend(cls.parse_num_list(n_str))
        n_list = list(set(n_list))  # revmove duplicated items
        return n_list


class PricePolicyHelper(object):
    @classmethod
    def map_price_policy(cls, pp_obj, inline_vc, ts):
        """ get responding price policy through inline_vc and ts.
        :param pp_obj: a dict like following:
            {inline_vc1: {start_month1: {time_range1: v,
                                         time_range2: v
                                        },
                          start_month2: {...}
                         },
             inline_vc2: {...}
            }
        :param inline_vc: the inline_vc of the data belonged to
        :param ts: the timestamp of the data, always the small timestamp of
            a time range.
        :return: v in the pp_obj format
        # NOTE: if no related price policy, return None
        """
        if len(pp_obj) == 0:
            return None

        # select by inline_vc
        key_vc = str(inline_vc)
        val_vc = pp_obj.get(key_vc)
        if val_vc is None:
            return None

        # select by start_month
        months = [int(i) for i in val_vc.keys()]
        months.sort(reverse=True)
        target_month = months[-1]
        for item in months:
            if ts > item:
                target_month = item
                break
        key_sm = str(target_month)
        val_sm = val_vc[key_sm]

        # select by time_range
        tr_dict = {}
        for item in val_sm.keys():
            month_str_l = [i.strip() for i in item.split(',')]
            m_list = []  # final format like: [1,2,3,4,9,10,11,12]
            for m_str in month_str_l:
                # get all month in m_list
                m_list.extend(NumListHelper.parse_num_list(m_str))
            for month in m_list:
                tr_dict[month] = item
        dt = pendulum.from_timestamp(ts, tz='Asia/Shanghai')
        key_tr = tr_dict[dt.month]
        pp_info_d = val_sm[key_tr]
        return pp_info_d

    @classmethod
    def quarter_chars_2_time_str(cls, quarters):
        """ convert quarter chars to time_str.
        quarter chars like: ("vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvfffffffffffff"
            "fffffffffffppppppppppppffffffffppppppppppppffffffff")
        time_str like: {'s': [],
                        'p': ['14:00-17:00', '19:00-22:00'],
                        'f': ['08:00-14:00', '17:00-19:00', '22:00-24:00'],
                        'v': ['00:00-08:00']}

        # NOTE: 22:00-24:00, the result has 24:00 that represents the last
        second of the day.
        """
        rlt = {'s': [], 'p': [], 'f': [], 'v': []}
        now = pendulum.now(tz='Asia/Shanghai')
        stime = pendulum.datetime(now.year, now.month, now.day,
                                  tz='Asia/Shanghai')
        etime = stime
        pre_char = quarters[0]
        quarters = quarters + 'x'  # add 'x' to trigger last chars saving
        for item in quarters:
            # char changing triggers saving previous chars
            if item != pre_char:
                st_str = stime.format("HH:mm")
                et_str = etime.format("HH:mm")
                if etime.hour == 0 and etime.minute == 0:
                    et_str = '24:00'
                rlt[pre_char].append(st_str + '-' + et_str)
                stime = etime
            pre_char = item
            etime = etime.add(minutes=15)
        return rlt
