from odoo import fields, models, api, _
from stdnum.au.acn import to_abn
from odoo.exceptions import ValidationError, UserError


class PaymentOrders(models.Model):
    _name = 'payment.orders'
    _description = "Payment Orders"
    _inherit = ['mail.thread', 'mail.activity.mixin']
    _order = 'payment_order_date desc'

    def _default_journal(self):
        setting = self.env['family.validation.setting'].search([], limit=1)
        return setting.payment_journal_id.id if setting and setting.journal_id else False

    name = fields.Char(string="Code", copy=False, readonly=True, default=lambda x: _('New'))
    # todo handel _compute_ref_num
    ref_num = fields.Many2one('seasonal.service', string='Ref. Number', compute='_compute_ref_num')
    payment_order_date = fields.Datetime(string="Payment Order Date", default=fields.Datetime.now)
    accountant_id = fields.Many2one('res.users', string='Accountant')
    # todo remove _compute_service_type_id and _inverse_service_type_id
    payment_order_description = fields.Many2one('services.settings',string='Payment Order Description',)
    service_requests_ids = fields.Many2many(comodel_name='service.request',
                                            relation='service_request_payment_order_rel',
                                            column1='payment_order_id', column2='service_request_id',
                                            string='Service Requests', )
    benefit_expense_line_ids = fields.One2many('benefit.expense.line', 'payment_order_id',
                                               string='Benefit Expense Lines')
    # todo need to remove seasonal_requests_ids
    seasonal_requests_ids = fields.One2many('seasonal.service', 'payment_order_id', string='Seasonal Requests')
    state = fields.Selection(string='Status',
                             selection=[('draft', 'Draft'),
                                        ('waiting_head', 'Waiting for Head of Expenses Department'),
                                        ('waiting_finance', 'Waiting for Financial Manager'),
                                        ('waiting_gm', 'Waiting for General Manager'),
                                        ('waiting_deposit', 'Waiting for Deposit'),
                                        ('done', 'Done'),
                                        ('refused', 'Refused')], default='draft', tracking=True)
    is_seasonal = fields.Boolean(string='Is Seasonal Service?')
    journal_id = fields.Many2one(comodel_name='account.journal', string="Journal", copy=False,
                                 domain="[('type','in',['cash','bank'])]", default=_default_journal)
    type = fields.Selection(
        selection=[
            ('services', 'Services'),
            ('seasonal_services', 'Seasonal Services'),
            ('benefit_expense', 'Monthly Expense'),
        ],
        string='Payment Order Type', required=True, default='services', )
    move_id = fields.Many2one('account.move', ondelete='cascade')
    move_count = fields.Integer(string="Move Count", compute='_compute_move_count')
    total_amount = fields.Float(string="Total Amount", compute='_compute_total_amount', store=True)
    company_id = fields.Many2one('res.company', string='Company', default=lambda self: self.env.company)
    currency_id = fields.Many2one('res.currency', string='Currency',
                                  related='company_id.currency_id')
    can_user_act = fields.Boolean(compute='_compute_can_user_act', store=False)
    attachment_line_count = fields.Integer(string="Required Attachments",compute='_compute_attachment_lines',store=False)

    def _compute_attachment_lines(self):
        for record in self:
            if not record.service_requests_ids:
                record.attachment_line_count = 0
                continue
            all_lines = record.service_requests_ids.mapped('attachment_lines')
            record.attachment_line_count = len(all_lines)

    def action_view_all_required_attachments(self):
        self.ensure_one()
        ctx = self.env.context.copy()
        ctx.update({
            'create': False, 'edit': False, 'delete': False,
        })
        return {
            'name': _('Service Required Attachments'),
            'type': 'ir.actions.act_window',
            'res_model': 'service.attachments.settings',
            'view_mode': 'tree,form',
            'domain': [('id', 'in', self.service_requests_ids.mapped('attachment_lines').ids)],
            'context': ctx,
            'target': 'current',
        }

    @api.depends('state')
    @api.depends_context('uid')
    def _compute_can_user_act(self):
        user = self.env.user
        for record in self:
            can_act = False
            if record.state in ['draft', 'waiting_deposit']:
                can_act = user.has_group('odex_benefit.group_benefit_payment_accountant_accept')
            elif record.state == 'waiting_head':
                can_act = user.has_group('odex25_account_payment_fix.group_depart_manager')
            elif record.state == 'waiting_finance':
                can_act = user.has_group('odex25_account_payment_fix.group_accounting_manager')
            elif record.state == 'waiting_gm':
                can_act = user.has_group('odex25_account_payment_fix.group_general_manager')

            record.can_user_act = can_act

    @api.depends('service_requests_ids', 'benefit_expense_line_ids')
    def _compute_total_amount(self):
        for rec in self:
            if rec.type in ['services', 'seasonal_services']:
                rec.total_amount = sum(rec.service_requests_ids.mapped('requested_service_amount'))
            elif rec.type == 'benefit_expense':
                rec.total_amount = sum(rec.benefit_expense_line_ids.mapped('total_family_expenses'))
            else:
                rec.total_amount = 0.0

    def action_manager_approval(self):
        for rec in self:
            if self.env.user != rec.accountant_id:
                raise ValidationError(_("Only the assigned accountant can approve this payment order."))
            rec.state = 'waiting_head'

    def action_head_approval(self):
        for rec in self:
            rec.state = 'waiting_finance'

    def action_finance_approval(self):
        for rec in self:
            rec.state = 'waiting_gm'

    def create_entry(self):
        for rec in self:
            if not rec.journal_id:
                raise UserError(_("Please select a journal before creating the entry."))
            line_ids = rec.get_lines()
            move_vals = {
                'journal_id': rec.journal_id.id,
                'line_ids': line_ids,
                'ref': rec.name + '/' + rec.type,
            }
            if rec.type == "benefit_expense":
                # ref1 = ", ".join(rec.benefit_expense_line_ids.mapped('name'))
                # ref2 = ", ".join(rec.benefit_expense_line_ids.mapped('family_expense_seq'))
                family_ids = rec.benefit_expense_line_ids.mapped('family_id').ids

                move_vals.update({
                    # 'ref': ref1 + '/' + ref2,
                    'benefit_family_ids': [(6, 0, family_ids)],
                })
            move_id = self.env['account.move'].create(move_vals)
            # move_id.action_post()
            rec.move_id = move_id

    def action_deposit(self):
        for rec in self:
            rec.state = 'done'

    def action_gm_approval(self):
        for rec in self:
            rec.state = 'waiting_deposit'

    def action_refuse(self):
        for rec in self:
            rec.state = 'refused'

    def action_reset_to_draft(self):
        for rec in self:
            rec.state = 'draft'

    # Compute method
    @api.depends('seasonal_requests_ids')
    def _compute_ref_num(self):
        for record in self:
            record.ref_num = record.seasonal_requests_ids[0] if record.seasonal_requests_ids else False

    @api.model
    def create(self, vals):
        res = super(PaymentOrders, self).create(vals)
        if not res.name or res.name == _('New'):
            res.name = self.env['ir.sequence'].sudo().next_by_code('payment.orders.sequence') or _('New')
        return res

    def unlink(self):
        for payment in self:
            if payment.state not in ['draft']:
                raise ValidationError(_(
                    "You cannot delete this payment order.\n"
                    "Only records in the *Draft* state may be deleted."
                ))
        return super(PaymentOrders, self).unlink()

    def _compute_move_count(self):
        for rec in self:
            rec.move_count = 1 if rec.move_id else 0

    def action_open_related_move_records(self):
        return {
            'name': _('Moves'),
            'type': 'ir.actions.act_window',
            'res_model': 'account.move',
            'view_mode': 'tree,form',
            'domain': [('id', 'in', self.move_id.ids)],
        }

    def _prepare_entry_lines(self, line, validation_setting):
        entry_lines = []
        total_credit_amount = 0
        expense_types = [
            ('family_monthly_income', 'cash_expense', validation_setting.cash_expense_account_id.id),
            ('family_monthly_meals', 'meal_expense', validation_setting.meal_expense_account_id.id),
            ('family_monthly_clotting', 'clothing_expense', validation_setting.clothing_expense_account_id.id),
        ]

        for field, expense_type, debit_account_id in expense_types:
            amount = getattr(line, field, 0.0)
            if amount:
                name = _("Family Code - %s Family Expense - %s - %s/%s") % (
                    line.family_id.code, expense_type, line.confirm_expense_id.name,
                    line.confirm_expense_id.family_expense_seq)
                entry_lines.append(self._create_debit_line(line, debit_account_id, amount, name))
                total_credit_amount += amount

        return entry_lines, total_credit_amount

    def _create_debit_line(self, line, account_id, amount, name):
        """Create a debit line"""
        return {
            'name': name,
            'family_confirm_id': line.confirm_expense_id.id,
            'benefit_family_id': line.family_id.id,
            'partner_id': line.family_id.partner_id.id,
            'analytic_account_id': line.family_id.branch_family_id.branch.analytic_account_id.id,
            'account_id': account_id,
            'debit': amount,
            'credit': 0.0,
        }

    def get_lines(self):
        lines = []
        total_credit = 0
        # credit_line_name = _("Total Credit for Family Expenses")
        if self.type in ['seasonal_services', 'services']:
            if self.service_requests_ids:
                for request in self.service_requests_ids:
                    lines.append(
                        {
                            'name': f'{_("Family code")}-{request.family_id.code}-{request.description}-{request.payment_order_id.name}-{request.payment_order_id.ref_num}',
                            'benefit_family_id': request.family_id.id,
                            'account_id': request.account_id.id,
                            'partner_id': request.family_id.partner_id.id,
                            'analytic_account_id': request.family_id.branch_family_id.branch.analytic_account_id.id,
                            'debit': request.requested_service_amount,
                            'credit': 0.0,
                        }
                    )
                    total_credit += request.requested_service_amount
        elif self.type == 'benefit_expense':
            validation_setting = self.env["family.validation.setting"].search([], limit=1)
            for line in self.benefit_expense_line_ids:
                sum_line, credit_total = self._prepare_entry_lines(line, validation_setting)
                total_credit += credit_total
                lines += sum_line

        payment_type = dict(self._fields['type']._description_selection(self.env)).get(self.type)
        lines.append({
            'account_id': self.journal_id.default_account_id.id,
            'name': f'{self.name}-{payment_type}',
            'credit': total_credit,
            'debit': 0.0,
        })
        return [(0, 0, line) for line in lines]
