# This file is part of Tryton.  The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.

import datetime as dt

from sql.aggregate import Sum

from trytond.i18n import gettext
from trytond.model import fields
from trytond.modules.account_invoice.exceptions import (
    InvoiceLineValidationError)
from trytond.pool import Pool, PoolMeta
from trytond.tools import grouped_slice, reduce_ids
from trytond.transaction import Transaction


class InvoiceLine(metaclass=PoolMeta):
    __name__ = 'account.invoice.line'

    project_invoice_works = fields.One2Many(
        'project.work', 'invoice_line',
        "Project Invoice Works", readonly=True)
    project_invoice_progresses = fields.One2Many(
        'project.work.invoiced_progress', 'invoice_line',
        "Project Invoice Progresses", readonly=True)
    project_invoice_timesheet_duration = fields.Function(
        fields.TimeDelta("Project Invoice Timesheet Duration"),
        'get_project_invoice_timesheet_duration')

    @classmethod
    def check_validate_project_invoice_quantity(cls, lines, field_names):
        pool = Pool()
        Lang = pool.get('ir.lang')
        if field_names and not (field_names & {
                    'quantity', 'project_invoice_works'}):
            return
        for line in lines:
            project_invoice_quantity = line.project_invoice_quantity
            if project_invoice_quantity is None:
                continue
            if line.unit:
                project_invoice_quantity = line.unit.round(
                    project_invoice_quantity)
            if line.quantity != project_invoice_quantity:
                lang = Lang.get()
                if line.unit:
                    quantity = lang.format_number_symbol(
                        project_invoice_quantity, line.unit)
                else:
                    quantity = lang.format_number(project_invoice_quantity)
                raise InvoiceLineValidationError(gettext(
                        'project_invoice.msg_project_invoice_line_quantity',
                        invoice_line=line.rec_name,
                        quantity=quantity,
                        ))

    @property
    def project_invoice_quantity(self):
        quantity = None
        for work in self.project_invoice_works:
            if quantity is None:
                quantity = 0
            if work.price_list_hour:
                quantity += work.effort_hours
            else:
                quantity += 1
        for progress in self.project_invoice_progresses:
            if quantity is None:
                quantity = 0
            work = progress.work
            if work.price_list_hour:
                quantity += progress.progress * work.effort_hours
            else:
                quantity += progress.progress
        if self.project_invoice_timesheet_duration is not None:
            if quantity is None:
                quantity = 0
            quantity += (
                self.project_invoice_timesheet_duration.total_seconds()
                / 60 / 60)
        return quantity

    @classmethod
    def get_project_invoice_timesheet_duration(cls, lines, name):
        pool = Pool()
        TimesheetLine = pool.get('timesheet.line')
        cursor = Transaction().connection.cursor()
        ts_line = TimesheetLine.__table__()

        durations = dict.fromkeys(map(int, lines))
        query = ts_line.select(
            ts_line.invoice_line, Sum(ts_line.duration),
            group_by=ts_line.invoice_line)
        for sub_lines in grouped_slice(lines):
            query.where = reduce_ids(
                ts_line.invoice_line, map(int, sub_lines))
            cursor.execute(*query)

            for line_id, duration in cursor:
                # SQLite uses float for SUM
                if (duration is not None
                        and not isinstance(duration, dt.timedelta)):
                    duration = dt.timedelta(seconds=duration)
                durations[line_id] = duration
        return durations

    @classmethod
    def validate_fields(cls, lines, field_names):
        super().validate_fields(lines, field_names)
        cls.check_validate_project_invoice_quantity(lines, field_names)

    @classmethod
    def delete(cls, lines):
        pool = Pool()
        WorkInvoicedProgress = pool.get('project.work.invoiced_progress')

        # Delete progress using root to skip access rule
        progress = []
        for sub_ids in grouped_slice([l.id for l in lines]):
            progress += WorkInvoicedProgress.search([
                    ('invoice_line', 'in', sub_ids),
                    ])
        if progress:
            with Transaction().set_user(0):
                WorkInvoicedProgress.delete(progress)

        super(InvoiceLine, cls).delete(lines)
