# © 2021 Le Filament (<http://www.le-filament.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from odoo import models, fields, api

from datetime import date


class ScopCotisation(models.AbstractModel):
    _name = "scop.cotisation"
    _description = "Base des cotisations"

    @api.model
    def default_get(self, fields):
        """
        Attribue la valeur des conditions de paiments par
        défault à la base de cotisation
        :param fields:
        :return:
        """
        res = super(ScopCotisation, self).default_get(fields)
        res['payment_term_id'] = self.env.user.company_id.\
            contribution_default_payment_term_id.id
        return res

    year = fields.Selection(
        [(year, str(year)) for year in range(
            fields.Datetime.now().year - 1, fields.Datetime.now().year + 2)],
        string='Année de cotisation',
        required=True)
    company_id = fields.Many2one(
        comodel_name='res.company',
        string='Company', change_default=True,
        required=True, readonly=True,
        default=lambda self: self.env.user.company_id)

    company_currency_id = fields.Many2one(
        comodel_name='res.currency', related='company_id.currency_id',
        string="Company Currency", readonly=True)
    payment_term_id = fields.Many2one(
        comodel_name='account.payment.term',
        string="Conditions de paiement",
        domain=[('is_contribution', '=', True)],
        required=True,
    )
    date_cotisation = fields.Date("Date de cotisation", required=True)

    member_count = fields.Integer(
        "Adhérents renouvelés",
        compute='_compute_member_count')
    new_member_count = fields.Integer(
        "Nouveaux adhérents",
        compute='_compute_new_member_count')
    trimester_1 = fields.Date('1er Trimestre')
    trimester_2 = fields.Date('2ème Trimestre')
    trimester_3 = fields.Date('3ème Trimestre')
    trimester_4 = fields.Date('4ème Trimestre')

    # ------------------------------------------------------
    # Compute fields
    # ------------------------------------------------------
    @api.multi
    def _compute_member_count(self):
        for cotiz in self:
            cotiz.member_count = len(cotiz.get_members())

    @api.multi
    def _compute_new_member_count(self):
        for cotiz in self:
            cotiz.new_member_count = len(cotiz.get_new_members())

    # ------------------------------------------------------
    # Global functions
    # ------------------------------------------------------
    def create_contribution(
            self, product, partner, type_contribution, liasse=None, amount=0,
            date=False, journal_id=False, account_id=False,
            type_invoice='out_invoice', is_regul=False, bordereau=False):
        """
        Create invoice from Contribution Base
        :param product: product_id
        :param partner: partner_id
        :param type_contribution: type_contribution (CG, UR, Fédé)
        :param liasse: liasse_fiscale_id (reference)
        :param amount: contribution amount (float)
        :param date: date invoice
        :param journal_id: journal
        :param account_id: customer_account_id
        :param type_invoice: invoice or refund
        :param is_regul: used for CG Scop regul
        :return: invoice
        """
        Invoice = self.env['account.invoice']
        InvoiceLine = self.env['account.invoice.line']

        domain = [
            ('partner_id', '=', partner.id),
            ('year', '=', self.year),
            ('type_contribution_id', '=', type_contribution),
        ]
        if bordereau:
            domain.append(('bordereau_id', '=', bordereau.id))

        exisiting_invoice = Invoice.search(domain)

        if not exisiting_invoice or is_regul:
            date_invoice = date if date else self.date_cotisation
            journal_id = self.company_id.contribution_journal_id \
                if not journal_id else journal_id
            account_id = partner.property_account_receivable_id \
                if not account_id else account_id
            member_invoice = Invoice.create({
                'partner_id': partner.id,
                'liasse_fiscale_id': liasse.id,
                'type': type_invoice,
                'year': self.year,
                'is_contribution': True,
                'type_contribution_id': type_contribution,
                'journal_id': journal_id.id,
                'state': 'draft',
                'account_id': account_id.id,
                'payment_term_id': self.payment_term_id.id,
                'payment_mode_id': partner.customer_payment_mode_id.id,
                'date_invoice': date_invoice,
            })
        else:
            member_invoice = exisiting_invoice

        # Création de la ligne CG Scop
        exisiting_invoice_line_ids = InvoiceLine.search([
            ('invoice_id', '=', member_invoice.id),
            ('product_id', '=', product.id)
        ])
        if not exisiting_invoice_line_ids or is_regul:
            InvoiceLine.create({
                'invoice_id': member_invoice.id,
                'product_id': product.id,
                'account_id': product.property_account_income_id.id,
                'invoice_line_tax_ids': [(6, 0, product.taxes_id.ids)],
                'name': product.name,
                'price_unit': amount
            })
        else:
            exisiting_invoice_line_ids[0].write({
                'price_unit': amount
            })

        return member_invoice

    @api.multi
    def get_members(self):
        self.ensure_one()
        members = self.env['scop.membership.period'].search([
            ('type_id', '=', self.env.ref(
                'cgscop_partner.membership_type_1').id),
            ('start', '<', date(self.year, 1, 1)),
            ('end', '=', None),
        ]).mapped('partner_id')
        return members

    @api.multi
    def get_new_members(self, limit_start_date=None):
        if not limit_start_date:
            limit_start_date = date(self.year, 12, 31)
        self.ensure_one()
        members = self.env['scop.membership.period'].search([
            ('type_id', '=', self.env.ref(
                'cgscop_partner.membership_type_1').id),
            ('start', '>=', date(self.year, 1, 1)),
            ('start', '<=', limit_start_date),
            '|',
            ('end', '=', None),
            ('end', '>', date(self.year, 1, 1))
        ]).mapped('partner_id')
        return members

    def round_to_closest_multiple(self, float_to_round, multiple):
        """
        :param float_to_round:
        :param multiple:
        :return: closest_multiple
        """
        small_multiple = (float_to_round // multiple) * multiple
        large_multiple = small_multiple + multiple

        # Return the closest of two
        if abs(float_to_round - small_multiple) < \
                abs(float_to_round - large_multiple):
            return small_multiple
        else:
            return large_multiple