from asyncore import write

from odoo import fields, models, api, _
from odoo.exceptions import UserError, ValidationError
from datetime import timedelta
from dateutil.relativedelta import relativedelta


class SeasonalService(models.Model):
    _name = 'seasonal.service'
    _inherit = ['mail.thread', 'mail.activity.mixin']
    _order = "name desc"

    name = fields.Char(string='Reference', required=True, copy=False, readonly=True, index=True,
                       default=lambda self: _('New'))
    date = fields.Datetime(string='Request Date', default=fields.Datetime.now, copy=False)
    service_type_id = fields.Many2one('services.settings', domain="[('is_seasonal_service','=',True)]",
                                      string="Seasonal Service Type", required=True)
    benefit_type = fields.Selection(string='Benefit Type', related='service_type_id.benefit_type')
    branch_ids = fields.Many2many('branch.settings', 'service_branch_rel', 'service_id', 'branch_id', string='Branches',
                                  required=True, domain="[('has_employees', '=', True)]")
    allowed_service_categories = fields.Many2many(
        comodel_name='benefit.category',
        related='service_type_id.benefit_category_ids',
        string="Allowed Categories",
        readonly=True
    )
    family_category_ids = fields.Many2many(
        'benefit.category',
        'service_category_rel',
        'service_id',
        'category_id',
        string='Family Category', domain="[('id', 'in', allowed_service_categories)]")
    aid_amount = fields.Float(string="Aid Amount", compute="_compute_amounts", store=True, readonly=False)
    benefit_member_count = fields.Integer(string="Benefit Member count", compute="compute_family_benefit", store=True)
    family_count = fields.Integer(string="Family count", compute="compute_family_benefit", store=True)
    family_disbursement_total_amount = fields.Float(string="Total Family Disbursement Amount",
                                                    compute="_compute_family_disbursement", store=True)
    payment_order_id = fields.Many2one('payment.orders', string='Payment Order', copy=False, ondelete="set null")
    payment_order_count = fields.Integer(compute='_compute_payment_order', string='Number of Payment Orders')
    vendor_bill = fields.Many2one('account.move', copy=False)
    total_moves = fields.Integer(string="Total Move", compute='_get_total_move_lines')
    state = fields.Selection(selection=[
        ('draft', 'Draft'),
        ('calculated', 'Calculated'),
        ('gm_assistant', 'Waiting Assistant General Manager'),
        ('accounting_approve', 'Accounting Approve'),
        ('waiting_receive', 'Waiting for Receive'),
        ('done', 'Done'),
    ], string='state', default='draft', tracking=True, copy=False)
    is_payment_order_done = fields.Boolean(string='Is Payment Order Done?', copy=False)
    payment_order_state = fields.Selection(string='Payment Order State', selection=[
        ('none', 'None'),
        ('waiting', 'Waiting Payment'),
        ('done', 'Done Payment'), ], copy=False, compute="_compute_payment_order_state", store=True)
    account_id = fields.Many2one('account.account', string='Expenses Account', related="service_type_id.account_id")
    service_requests_ids = fields.One2many('service.request', 'seasonal_service_id', string='Service Requests')
    benefit_ids = fields.Many2many(comodel_name='grant.benefit', relation='benefit_grant_seasonal_service_rel',
                                   column1='seasonal_service_id',
                                   column2='benefit_id', string='Families', copy=False)
    member_ids = fields.Many2many(comodel_name='family.member', relation='family_member_seasonal_service_rel',
                                  column1='seasonal_service_id',
                                  column2='family_member_id', string='Family Members', copy=False)
    family_domain_ids = fields.Many2many(comodel_name='grant.benefit', compute='_compute_domain_ids',
                                         string="Eligible Families")
    allowed_member_ids = fields.Many2many('family.member', compute='_compute_allowed_member_ids',
                                          string="Allowed Members", )
    service_delivery_method = fields.Selection(selection=[
        ('cash', 'Cash'),
        ('in_kind', 'In kind'), ], string='Service Delivery Method', default='cash')
    is_in_kind = fields.Boolean(string="In Kind", related="service_type_id.in_kind")
    company_id = fields.Many2one('res.company', string="Company", default=lambda self: self.env.user.company_id)
    currency_id = fields.Many2one('res.currency', string="Currency", related='company_id.currency_id')

    @api.depends('payment_order_id', 'payment_order_id.state', 'vendor_bill', 'vendor_bill.state')
    def _compute_payment_order_state(self):
        for rec in self:
            payment_order_state = 'none'
            if rec.payment_order_id:
                if rec.payment_order_id.state == "done":
                    payment_order_state = "done"
                    rec.state = 'waiting_receive'
                else:
                    payment_order_state = "waiting"
            elif rec.vendor_bill:
                if rec.vendor_bill.state == "posted":
                    payment_order_state = "done"
                    rec.state = 'waiting_receive'
                else:
                    payment_order_state = "waiting"
            rec.payment_order_state = payment_order_state

    @api.depends('payment_order_id')
    def _compute_payment_order(self):
        for rec in self:
            if rec.payment_order_id:
                rec.payment_order_count = 1
            else:
                rec.payment_order_count = 0

    @api.depends('vendor_bill')
    def _get_total_move_lines(self):
        for rec in self:
            if rec.vendor_bill:
                moves = 1
            else:
                moves = 0
            rec.total_moves = moves

    @api.depends('benefit_ids', 'service_type_id')
    def _compute_allowed_member_ids(self):
        for rec in self:
            domain = [('benefit_id', 'in', rec.benefit_ids.ids),
                      ('member_status', '=', 'benefit')]

            if rec.service_type_id.max_age:
                domain.append(('age', '<=', rec.service_type_id.max_age))

            members = self.env['family.member'].sudo().search(domain)

            if rec.service_type_id.is_this_service_for_student:
                members = members.filtered(
                    lambda m: (
                            m.education_status == 'educated'
                            and any(edu.case_study == 'continuous'
                                    for edu in m.member_education_status_ids)
                    )
                )

            rec.allowed_member_ids = members

    @api.onchange('service_type_id')
    def _onchange_service_type_id(self):
        if self.benefit_ids:
            self.benefit_ids = [(5, 0, 0)]
        self.family_category_ids = [(5, 0, 0)]
        if self.service_type_id and self.service_type_id.benefit_category_ids:
            self.family_category_ids = [(6, 0, self.service_type_id.benefit_category_ids.ids)]

    @api.depends('branch_ids', 'family_category_ids')
    def _compute_domain_ids(self):
        for rec in self:
            domain = ['|', ('state', '=', 'second_approve'), '&', ('state', 'in', ('waiting_approve', 'first_approve')),
                      ('action_type', '=', 'suspended')]
            if self.branch_ids:
                domain.append(('branch_custom_id', 'in', self.branch_ids.ids))
            if self.family_category_ids:
                domain.append(('benefit_category_id', 'in', self.family_category_ids.ids))

            families = self.env['grant.benefit'].sudo().search(domain)
            rec.family_domain_ids = families

    @api.onchange('branch_ids', 'family_category_ids')
    def _onchange_cleanup_families_members(self):
        if not self.branch_ids or not self.family_category_ids:
            self.benefit_ids = [(5, 0, 0)]
            return

        allowed_branch_ids = set(self.branch_ids.ids)
        allowed_category_ids = set(self.family_category_ids.ids)
        families_to_keep = self.benefit_ids.filtered(
            lambda b: bool(b.branch_custom_id and b.branch_custom_id.id in allowed_branch_ids)
                      and bool(b.benefit_category_id and b.benefit_category_id.id in allowed_category_ids)
        )

        # apply families kept (replace whole m2m)
        if len(families_to_keep) != len(self.benefit_ids):
            self.benefit_ids = [(6, 0, families_to_keep.ids)]

    @api.onchange('benefit_ids')
    def _onchange_benefit_ids_cleanup_members(self):
        if not self.benefit_ids:
            self.member_ids = [(5, 0, 0)]
            return

        current_family_ids = set(self.benefit_ids.ids)
        members_to_keep = self.member_ids.filtered(
            lambda m: m.benefit_id and m.benefit_id.id in current_family_ids
        )
        if len(members_to_keep) != len(self.member_ids):
            self.member_ids = [(6, 0, members_to_keep.ids)]

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

    def unlink(self):
        for service in self:
            if service.state not in ['draft']:
                raise UserError(_('You cannot delete this record'))
        return super(SeasonalService, self).unlink()

    def action_create_payment_order(self):
        for rec in self:
            if rec.state != 'accounting_approve':
                raise UserError(_("All selected requests should be in Accounting Approve state"))
            if rec.payment_order_id:
                raise UserError(_("There are already disbursement orders associated with the request(s)."))
            self.env['payment.orders'].create({
                'state': 'draft',
                'accountant_id': rec.service_type_id.accountant_id.id,
                'seasonal_requests_ids': rec.ids,
                'service_requests_ids': rec.service_requests_ids.ids,
                'is_seasonal': True,
                'type': 'seasonal_services',
                'payment_order_description': rec.service_type_id.id,
            })
            # rec.payment_order_id = payment_order.id
            rec.is_payment_order_done = True

    def action_accounting_transfer(self):
        validation_setting = self.env["family.validation.setting"].search([], limit=1)
        line_ids = []
        for rec in self:
            if rec.service_type_id.payment_method == "payment_order":
                if rec.payment_order_id:
                    raise UserError(_(
                        "A payment order is already linked to this request: %s"
                    ) % rec.name)
                payment_order = self.env['payment.orders'].create({
                    'state': 'draft',
                    'accountant_id': rec.service_type_id.accountant_id.id,
                    'seasonal_requests_ids': rec.ids,
                    'service_requests_ids': rec.service_requests_ids.ids,
                    'is_seasonal': True,
                    'type': 'seasonal_services',
                    'payment_order_description':rec.service_type_id.id,
                })
                rec.payment_order_id = payment_order.id
            elif rec.service_type_id.payment_method == "invoice":
                if rec.vendor_bill:
                    raise UserError(_(
                        "A vendor bill is already linked to this request: %s"
                    ) % rec.name)
                if not rec.service_requests_ids:
                    raise UserError(_(
                        "No service requests found to generate a vendor bill for: %s"
                    ) % rec.name)
                for request in rec.service_requests_ids:
                    invoice_line = (0, 0, {
                        'name': f'{request.family_id.name}/{request.description}-{request.name}',
                        'benefit_family_id': request.family_id.id,
                        'account_id': request.account_id.id,
                        'analytic_account_id': request.family_id.branch_family_id.branch.analytic_account_id.id,
                        'quantity': request.requested_quantity or 1,  # todo check
                        'price_unit': request.requested_service_amount,
                    })
                    line_ids.append(invoice_line)
                vendor_bill = self.env['account.move'].sudo().create({
                    'move_type': 'in_invoice',
                    'partner_id': rec.service_type_id.service_producer_id.id,
                    'journal_id': validation_setting.journal_id.id,
                    'invoice_line_ids': line_ids,
                })
                rec.vendor_bill = vendor_bill

    @api.depends('service_requests_ids')
    def compute_family_benefit(self):
        for record in self:
            families = record.service_requests_ids.mapped('family_id')
            family_count = len(families)
            member_count = sum(record.service_requests_ids.mapped('service_benefit_count'))
            record.family_count = family_count
            record.benefit_member_count = member_count

    @api.depends('service_type_id')
    def _compute_amounts(self):
        for record in self:
            if record.service_type_id and record.service_delivery_method == 'cash':
                record.aid_amount = record.service_type_id.max_amount
            else:
                record.aid_amount = 0

    @api.depends('service_requests_ids')
    def _compute_family_disbursement(self):
        for record in self:
            if record.service_requests_ids:
                if record.service_delivery_method == "cash":
                    record.family_disbursement_total_amount = sum(
                        record.service_requests_ids.mapped('requested_service_amount'))
                else:
                    record.family_disbursement_total_amount = sum(
                        record.service_requests_ids.mapped('service_qty'))
            else:
                record.family_disbursement_total_amount = 0.0

    def action_first_approval(self):
        for rec in self:
            if not rec.service_requests_ids:
                raise UserError(_("You must add at least one service request."))
            if rec.service_type_id.needs_beneficiary_manager_approval:
                rec.state = 'gm_assistant'
                rec.service_requests_ids.write({'state': 'gm_assistant'})
            elif rec.service_delivery_method == "cash":
                rec.action_accounting_transfer()
                rec.state = "accounting_approve"
                rec.service_requests_ids.write({'state': 'accounting_approve'})
            elif rec.service_delivery_method == "in_kind":
                rec.state = 'waiting_receive'
                rec.service_requests_ids.write({'state': 'send_request_to_supplier'})

    def action_approval_of_gm_assistant(self):
        for rec in self:
            if rec.service_delivery_method == "cash":
                rec.action_accounting_transfer()
                rec.state = "accounting_approve"
                rec.service_requests_ids.write({'state': 'accounting_approve'})
            elif rec.service_delivery_method == "in_kind":
                rec.state = 'waiting_receive'
                rec.service_requests_ids.write({'state': 'send_request_to_supplier'})

    def action_done(self):
        for rec in self:
            rec.state = 'done'
            rec.service_requests_ids.write({'state': 'family_received_device'})

    def action_calculate(self):
        for rec in self:
            rec._generate_service_requests()
            rec.state = 'calculated'

    def action_recalculate(self):
        for rec in self:
            rec.service_requests_ids.unlink()
            rec._generate_service_requests()

    def _generate_service_requests(self):
        Service = self.env['service.request']

        for rec in self:
            if not rec.benefit_ids:
                raise UserError(_("You must add at least one family."))

            if rec.benefit_type == "family":
                for benefit in rec.benefit_ids:
                    service_request = Service.create({
                        'family_id': benefit.id,
                        'service_cat': rec.service_type_id.id,
                        'seasonal_service_id': rec.id,
                        'benefit_type': rec.benefit_type,
                    })
                    service_request.onchange_requested_service_amount()

                    if rec.service_delivery_method == "cash":
                        service_request.requested_service_amount = service_request.service_max_amount
                        if service_request.requested_service_amount == 0:
                            service_request.unlink()
                    else:
                        service_request.requested_service_amount = 0
                        service_request.is_in_kind = True
                        service_request.service_qty = rec.aid_amount

            else:
                if not rec.member_ids:
                    raise UserError(_("You must add at least one member."))
                for member in rec.member_ids:
                    if not member.benefit_id:
                        raise UserError(_("Member %s has no related family (benefit).") % member.name)

                    service_request = Service.create({
                        'family_id': member.benefit_id.id,
                        'member_id': member.id,
                        'service_cat': rec.service_type_id.id,
                        'seasonal_service_id': rec.id,
                        'benefit_type': rec.benefit_type,
                    })

                    service_request.onchange_requested_service_amount()

                    if rec.service_delivery_method == "cash":
                        service_request.requested_service_amount = service_request.service_max_amount
                        if service_request.requested_service_amount == 0:
                            service_request.unlink()
                    else:
                        service_request.requested_service_amount = 0
                        service_request.is_in_kind = True
                        service_request.service_qty = rec.aid_amount

    def action_reset_to_draft(self):
        for rec in self:
            rec.service_requests_ids.write({'state': 'draft'})
            rec.service_requests_ids.unlink()
            rec.state = 'draft'

    def action_reset_to_calculated(self):
        self.ensure_one()
        return {
            'name': _('Reason for Return'),
            'type': 'ir.actions.act_window',
            'res_model': 'reason.for.return.wizard',
            'view_mode': 'form',
            'target': 'new',
        }

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

    def action_open_payment_orders(self):
        self.ensure_one()
        if not self.payment_order_id:
            raise UserError(_("No payment order are linked to this request."))

        return {
            'name': _('Payment Orders'),
            'type': 'ir.actions.act_window',
            'res_model': 'payment.orders',
            'view_mode': 'tree,form',
            'domain': [('id', 'in', self.payment_order_id.ids)],
            'context': {'create': False},
        }
