# -*- coding: utf-8 -*-

import re
import os
from datetime import datetime, timedelta, timezone
from tzlocal import get_localzone

from .exceptions import ArgumentException

# Auxiliary

DATETIME_FORMAT = r'%Y-%m-%d %H:%M:%S %z'


def td2str(td):
    remainder = td.seconds + 3600 * td.days
    hours, remainder = divmod(remainder, 3600)
    minutes, seconds = divmod(remainder, 60)

    s = ""
    if hours != 0:
        s += "%sh" % hours
    if minutes != 0:
        s += "%s%smin" % (' ' if s else '', minutes)
    if seconds != 0:
        s += "%s%ss" % (' ' if s else '', seconds)
    return s


def dt2str(dt):
    return dt.strftime(DATETIME_FORMAT)


def str2dt(string):
    return datetime.strptime(string, DATETIME_FORMAT)

# The function `now` needs to be mocked when running tests
# In order to have progressing time, we maintain a file with an incrementing
# integer
if os.path.isfile('DIT_TESTING'):

    LOCALZONE = timezone(timedelta(-1, 79200), 'BRST')
    TODAY = datetime(2016, 9, 10, 0, 0, 0, 0, LOCALZONE)

    _base_now = datetime(2016, 9, 10, 18, 50, 43, 0, LOCALZONE)

    def now():
        with open('DIT_TESTING', 'r') as f:
            i = int(f.read())
        now = _base_now + timedelta(seconds=(i * 40))
        with open('DIT_TESTING', 'w') as f:
            f.write(str(i + 1))
        return now

else:
    LOCALZONE = get_localzone()
    TODAY = datetime.now(LOCALZONE).replace(hour=0,
                                            minute=0,
                                            second=0,
                                            microsecond=0)

    def now():
        return datetime.now(LOCALZONE)


def now_str():
    return dt2str(now())


def time_spent_on(logbook):
    time_spent = timedelta()
    for log in logbook:
        if log['out']:
            time_spent += log['out'] - log['in']
    return time_spent


def convert_datetimes(data):
    for key in ['created_at', 'updated_at', 'concluded_at']:
        if key in data:
            data[key] = str2dt(data[key])
    logbook = data.get('logbook', [])
    for log in logbook:
        log['in'] = str2dt(log['in'])
        log['out'] = str2dt(log['out']) if log['out'] else None
    return data

# ===========================================
# Filtering


def _interpret_date(string):

    if string in ["now"]:
        return now()
    elif string in ["today", "td"]:
        return TODAY
    elif string == ["yesterday", "yd"]:
        return TODAY - timedelta(days=-1)

    days_p = r'(?P<days>[+-]?\d+) ?d(ays?)?'
    hours_p = r'(?P<hours>[+-]?\d+) ?h(ours?)?'
    minutes_p = r'(?P<minutes>[+-]?\d+) ?min(utes?)?'

    days_m = re.search(days_p, string)
    hours_m = re.search(hours_p, string)
    minutes_m = re.search(minutes_p, string)

    if days_m or hours_m or minutes_m:
        dt = TODAY
        if days_m:
            dt += timedelta(days=int(days_m.group('days')))
        if hours_m:
            dt += timedelta(hours=int(hours_m.group('hours')))
        if minutes_m:
            dt += timedelta(minutes=int(minutes_m.group('minutes')))
        return dt

    raise ArgumentException('Unrecognized date/time string: %s' % string)


def apply_filter_where(data, where):
    name = where[0]
    value = where[1]

    properties = data.get('properties', {})

    if name in properties and re.search(value, properties[name]):
        return data
    return {}


def apply_filter_to(data, to):
    if 'created_at' in data and to <= data['created_at']:
        return {}
    logbook = data.get('logbook', [])

    i = 0
    for i in range(0, -len(logbook), -1):
        if logbook[i - 1]['in'] <= to:
            break
    if i < 0:
        data['logbook'] = logbook[:i]
    return data


def apply_filter_from(data, fron):
    if 'concluded_at' in data and fron >= data['concluded_at'] or \
            'updated_at' in data and fron >= data['updated_at']:
        return {}
    logbook = data.get('logbook', [])

    for i in range(len(logbook) + 1):
        if i == len(logbook) or logbook[i]['in'] >= fron:
            break
    if i == len(logbook):
        data['logbook'] = []
    elif i > 0:
        data['logbook'] = logbook[i:]
    return data


def init_filters(filters):
    for key in filters:
        if key in ['from', 'to']:
            filters[key] = _interpret_date(filters[key])
        elif key == 'where':
            filters[key][1] = re.compile(filters[key][1])


def apply_filters(data, filters):

    if data and 'where' in filters:
        data = apply_filter_where(data, filters['where'])

    if data and 'from' in filters:
        data = apply_filter_from(data, filters['from'])

    if data and 'to' in filters:
        data = apply_filter_to(data, filters['to'])

    return data

@@ -3405,18 +3405,20 @@
 o(data, 
-to
+date
 ):%0A    i
@@ -3444,18 +3444,20 @@
 ata and 
-to
+date
  %3C= data
@@ -3533,18 +3533,8 @@
 %5D)%0A%0A
-    i = 0%0A
     
@@ -3552,12 +3552,8 @@
 nge(
-0, -
 len(
@@ -3564,11 +3564,11 @@
 ook)
-, -
+ + 
 1):%0A
@@ -3578,24 +3578,45 @@
      if 
+i == len(logbook) or 
 logbook%5B
 i - 1%5D%5B'
@@ -3607,16 +3607,17 @@
 logbook%5B
+-
 i - 1%5D%5B'
@@ -3624,18 +3624,20 @@
 in'%5D %3C= 
-to
+date
 :%0A      
@@ -3657,17 +3657,74 @@
    if i 
-%3C
+== len(logbook):%0A        data%5B'logbook'%5D = %5B%5D%0A    elif i %3E
  0:%0A    
@@ -3754,16 +3754,17 @@
 ogbook%5B:
+-
 i%5D%0A    r
@@ -3804,20 +3804,20 @@
 m(data, 
-fron
+date
 ):%0A    i
@@ -3837,36 +3837,36 @@
 at' in data and 
-fron
+date
  %3E= data%5B'conclu
@@ -3916,20 +3916,20 @@
 ata and 
-fron
+date
  %3E= data
@@ -4095,12 +4095,12 @@
  %3E= 
-fron
+date
 :%0A  

Improve uniformity between filter functions