# -*- coding: utf-8 -*-

from dateutil.relativedelta import relativedelta

from odoo import api, fields, models, _
from odoo.fields import Datetime
from odoo.osv.expression import NEGATIVE_TERM_OPERATORS


class MarketingParticipant(models.Model):
    _name = 'marketing.participant'
    _description = 'Marketing Participant'
    _rec_name = 'resource_ref'

    @api.model
    def default_get(self, default_fields):
        defaults = super(MarketingParticipant, self).default_get(default_fields)
        if 'res_id' in default_fields and not defaults.get('res_id'):
            model_name = defaults.get('model_name')
            if not model_name and defaults.get('campaign_id'):
                model_name = self.env['marketing.campaign'].browse(defaults['campaign_id']).model_name
            if model_name and model_name in self.env:
                resource = self.env[model_name].search([], limit=1)
                defaults['res_id'] = resource.id
        return defaults

    @api.model
    def _selection_target_model(self):
        models = self.env['ir.model'].search([('is_mail_thread', '=', True)])
        return [(model.model, model.name) for model in models]

    def _search_resource_ref(self, operator, value):
        ir_models = set([model['model_name'] for model in self.env['marketing.campaign'].search([]).read(['model_name'])])
        ir_model_ids = []
        for model in ir_models:
            if model in self.env:
                ir_model_ids += self.env['marketing.participant'].search(['&', ('model_name', '=', model), ('res_id', 'in', [name[0] for name in self.env[model].name_search(name=value)])]).ids
        operator = 'not in' if operator in NEGATIVE_TERM_OPERATORS else 'in'
        return [('id', operator, ir_model_ids)]

    campaign_id = fields.Many2one(
        'marketing.campaign', string='Campaign',
        index=True, ondelete='cascade', required=True)
    model_id = fields.Many2one(
        'ir.model', string='Model', related='campaign_id.model_id',
        index=True, readonly=True, store=True)
    model_name = fields.Char(
        string='Record model', related='campaign_id.model_id.model',
        readonly=True, store=True)
    res_id = fields.Integer(string='Record ID', index=True)
    resource_ref = fields.Reference(
        string='Record', selection='_selection_target_model',
        compute='_compute_resource_ref', inverse='_set_resource_ref', search='_search_resource_ref')
    trace_ids = fields.One2many('marketing.trace', 'participant_id', string='Actions')
    state = fields.Selection([
        ('running', 'Running'),
        ('completed', 'Completed'),
        ('unlinked', 'Removed'),
        ], default='running', index=True, required=True,
        help='Removed means the related record does not exist anymore.')
    is_test = fields.Boolean('Test Record', default=False)

    @api.depends('model_name', 'res_id')
    def _compute_resource_ref(self):
        for participant in self:
            if participant.model_name and participant.model_name in self.env:
                participant.resource_ref = '%s,%s' % (participant.model_name, participant.res_id or 0)
            else:
                participant.resource_ref = None

    def _set_resource_ref(self):
        for participant in self:
            if participant.resource_ref:
                participant.res_id = participant.resource_ref.id

    def check_completed(self):
        existing_traces = self.env['marketing.trace'].search([
            ('participant_id', 'in', self.ids),
            ('state', '=', 'scheduled'),
        ])
        (self - existing_traces.mapped('participant_id')).write({'state': 'completed'})

    @api.model
    def create(self, values):
        res = super(MarketingParticipant, self).create(values)
        # prepare first traces related to begin activities
        primary_activities = res.campaign_id.marketing_activity_ids.filtered(lambda act: act.trigger_type == 'begin')
        now = Datetime.now()
        trace_ids = [
            (0, 0, {
                'activity_id': activity.id,
                'schedule_date': now + relativedelta(**{activity.interval_type: activity.interval_number}),
            }) for activity in primary_activities]
        res.write({'trace_ids': trace_ids})
        return res

    def action_set_completed(self):
        ''' Manually mark as a completed and cancel every scheduled trace '''
        # TDE TODO: delegate set Canceled to trace record
        self.write({'state': 'completed'})
        self.env['marketing.trace'].search([
            ('participant_id', 'in', self.ids),
            ('state', '=', 'scheduled')
        ]).write({
            'state': 'canceled',
            'schedule_date': Datetime.now(),
            'state_msg': _('Marked as completed')
        })

    def action_set_running(self):
        self.write({'state': 'running'})

    def action_set_unlink(self):
        self.write({'state': 'unlinked'})
        self.env['marketing.trace'].search([
            ('participant_id', 'in', self.ids),
            ('state', '=', 'scheduled')
        ]).write({
            'state': 'canceled',
            'state_msg': _('Record deleted'),
        })
        return True
