"""Models for the freckle_budgets app."""
import calendar
import datetime

from django.db import models
from django.db.models import Sum
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _


@python_2_unicode_compatible
class Employee(models.Model):
    name = models.CharField(max_length=256, verbose_name=_('Name'))
    freckle_id = models.CharField(max_length=256, verbose_name=_('Freckle ID'))

    class Meta:
        ordering = ['name', ]

    def __str__(self):
        return str(self.name)

    def get_employee_project_months(self, month):
        """
        Returns the projects this employee is responsible for in this month.

        """
        return EmployeeProjectMonth.objects.filter(
            employee=self, project_month__month=month)


@python_2_unicode_compatible
class FreeTime(models.Model):
    employee = models.ForeignKey(Employee, verbose_name=_('Employee'))
    day = models.DateField(
        verbose_name=_('Day'))
    is_public_holiday = models.BooleanField(
        verbose_name=_('Is public holiday'), default=False)
    is_sick_leave = models.BooleanField(
        verbose_name=_('Is sick leave'), default=False)

    class Meta:
        ordering = ['employee', '-day']

    def __str__(self):
        return '{0} - {1}'.format(self.employee, self.day)


@python_2_unicode_compatible
class Year(models.Model):
    year = models.PositiveIntegerField(verbose_name=_('Year'))
    rate = models.FloatField(default=100, verbose_name=_('Rate'))
    work_hours_per_day = models.FloatField(
        default=5, verbose_name=_('Work hours per day'))
    sick_leave_days = models.PositiveIntegerField(
        verbose_name=_('Sick leave days'))
    vacation_days = models.PositiveIntegerField(
        verbose_name=_('Vacation days'))

    class Meta:
        ordering = ['-year', ]

    def __str__(self):
        return str(self.year)

    def get_total_cashflow_budget(self):
        return ProjectMonth.objects.filter(
            month__year=self, project__is_investment=False).aggregate(
                Sum('budget'))['budget__sum']

    def get_total_investment_budget(self):
        return ProjectMonth.objects.filter(
            month__year=self, project__is_investment=True).aggregate(
                Sum('budget'))['budget__sum']

    def get_total_unused_budget(self):
        result = 0
        for month in Month.objects.filter(year=self):
            result += month.get_unused_budget()
        return result


class MonthManager(models.Manager):
    def get_months_with_projects(self, year):
        qs = self.get_queryset()
        return qs.filter(year__year=year, projectmonth__isnull=False).order_by(
            'month').distinct()


@python_2_unicode_compatible
class Month(models.Model):
    year = models.ForeignKey(Year, verbose_name=_('Year'))
    month = models.PositiveIntegerField(verbose_name=_('Month'))
    employees = models.FloatField(
        default=1, verbose_name=_('Number of employees'))
    public_holidays = models.PositiveIntegerField(
        verbose_name=_('Public holydays'), default=0, null=True)

    objects = MonthManager()

    class Meta:
        ordering = ['-year', 'month']

    def __str__(self):
        return '{0} {1}'.format(self.month, self.year)

    def get_available_ressources(self):
        """Returns the available hours that the team can work this month."""
        work_days = self.get_work_days()
        work_hours = self.year.work_hours_per_day * work_days * self.employees
        return work_hours

    def get_available_ressources_max(self):
        """Returns the available hours that the team can work this month."""
        work_days = self.get_work_days(
            minus_sick_leave=False, minus_vacations=False)
        work_hours = self.year.work_hours_per_day * work_days * self.employees
        return work_hours

    def get_available_ressources_max_per_person(self):
        """Returns the hours that each employee should work this month."""
        return self.get_available_ressources_max() / self.employees * 1.0

    def get_average_rate(self):
        """Returns the average rate for all cashflow projects of this month."""
        return self.get_total_profit() / self.get_total_cashflow_hours()

    def get_cashflow_projects(self):
        return ProjectMonth.objects.filter(month=self).exclude(
            project__is_investment=True).order_by('-budget', )

    def get_date(self):
        return datetime.date(self.year.year, self.month, 1)

    def get_employees(self):
        """
        Returns all employees that have projects scheduled for this month.

        """
        employee_pks = EmployeeProjectMonth.objects.filter(
            project_month__month=self).values_list(
                'employee__pk', flat=True).distinct()
        return Employee.objects.filter(pk__in=employee_pks)

    def get_employee_public_holiday_days(self, employee):
        """Returns public holidays for given employee and this month."""
        return self.get_free_time_for_employee(employee).filter(
            is_public_holiday=True)

    def get_employee_sick_leave_days(self, employee):
        """Returns sick leave days for the given employee and this month."""
        return self.get_free_time_for_employee(employee).filter(
            is_sick_leave=True)

    def get_employee_vacation_days(self, employee):
        """Returns vacation days for given employee and this month."""
        return self.get_free_time_for_employee(employee).exclude(
            is_sick_leave=True).exclude(is_public_holiday=True)

    def get_free_time_for_employee(self, employee):
     
   """Returns the free times for this month and the given employee."""
        month_start = datetime.date(self.year.year, self.month, 1)
        month_end =
 datetime.date(
            self.year.year, self.month,
            calendar.monthrange(self.year.year, self.month)[1])
        free_time = FreeTime.objects.filter(
            employee=employee, day__gte=month_start, day__lte=month_end)
        return free_time

    def get_investment_projects(self):
        return ProjectMonth.objects.filter(
            month=self, project__is_investment=True).order_by('-budget', )

    def get_total_cashflow_hours(self):
        projects = self.get_cashflow_projects()
        hours = 0
        for project in projects:
            hours += project.get_budget_hours()
        return hours

    def get_total_investment_hours(self):
        projects = self.get_investment_projects()
        hours = 0
        for project in projects:
            hours += project.get_budget_hours()
        return hours

    def get_total_investment(self):
        projects = self.get_investment_projects()
        profit = 0
        for project in projects:
            profit += project.budget
        return profit

    def get_total_profit(self):
        projects = self.get_cashflow_projects()
        profit = 0
        for project in projects:
            profit += project.budget
        return profit

    def get_unused_hours(self):
        projects = ProjectMonth.objects.filter(month=self)
        used_hours = 0
        for project in projects:
            used_hours += project.get_budget_hours()
        unused_hours = self.get_available_ressources() - used_hours
        return unused_hours

    def get_unused_daily_hours(self):
        unused_hours = self.get_unused_hours()
        return unused_hours / self.get_work_days()

    def get_unused_budget(self):
        return self.get_unused_hours() * self.year.rate

    def get_weekdays(self):
        """Returns the number of weekdays of this month."""
        month_dates = calendar.Calendar(0).itermonthdates(
            self.year.year, self.month)
        weekdays = 0
        for day in month_dates:
            if day.month != self.month:
                continue
            if day.weekday() < 5:
                weekdays += 1
        return weekdays

    def get_work_days(self, minus_sick_leave=True, minus_vacations=True):
        """
        Returns the work days that should be used in whole-year-calculations.

        Public holidays, sick leave day