# This file is part of Tryton.  The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from collections import defaultdict
from textwrap import TextWrapper

from sql import Null
from sql.conditionals import Case

from trytond.i18n import lazy_gettext
from trytond.model import ModelSQL, ModelView, fields
from trytond.pool import Pool
from trytond.pyson import Eval
from trytond.transaction import Transaction

from .resource import ResourceMixin, resource_copy

__all__ = ['NoteCopyMixin']


class Note(ResourceMixin, ModelSQL, ModelView):
    __name__ = 'ir.note'
    message = fields.Text('Message', states={
            'readonly': Eval('id', 0) > 0,
            })
    message_wrapped = fields.Function(fields.Text('Message'),
        'on_change_with_message_wrapped')
    unread = fields.Function(fields.Boolean('Unread'), 'get_unread',
        searcher='search_unread', setter='set_unread')

    @staticmethod
    def default_unread():
        return False

    @classmethod
    def get_wrapper(cls):
        return TextWrapper(width=79)

    @fields.depends('message')
    def on_change_with_message_wrapped(self, name=None):
        wrapper = self.get_wrapper()
        message = self.message or ''
        return '\n'.join(map(wrapper.fill, message.splitlines()))

    @classmethod
    def get_unread(cls, notes, name):
        pool = Pool()
        Read = pool.get('ir.note.read')
        cursor = Transaction().connection.cursor()
        user_id = Transaction().user
        table = cls.__table__()
        read = Read.__table__()

        unread = defaultdict(bool)
        query = table.join(read, 'LEFT',
            condition=(table.id == read.note)
            & (read.user == user_id)
            ).select(table.id,
                Case((read.user != Null, False), else_=True),
                where=fields.SQL_OPERATORS['in'](
                    table.id, map(int, notes)))
        cursor.execute(*query)
        unread.update(cursor)
        return unread

    @classmethod
    def search_unread(cls, name, clause):
        pool = Pool()
        Read = pool.get('ir.note.read')
        user_id = Transaction().user
        table = cls.__table__()
        read = Read.__table__()

        _, operator, value = clause
        assert operator in ['=', '!=']
        Operator = fields.SQL_OPERATORS[operator]

        where = Operator(Case((read.user != Null, False), else_=True), value)
        query = table.join(read, 'LEFT',
            condition=(table.id == read.note)
            & (read.user == user_id)
            ).select(table.id, where=where)
        return [('id', 'in', query)]

    @classmethod
    def set_unread(cls, notes, name, value):
        pool = Pool()
        Read = pool.get('ir.note.read')
        user_id = Transaction().user
        if not value:
            Read.create([{'note': n.id, 'user': user_id} for n in notes])
        else:
            reads = Read.search([
                    ('note', 'in', notes),
                    ('user', '=', user_id),
                    ])
            Read.delete(reads)

    @classmethod
    def write(cls, notes, values, *args):
        # Avoid changing write meta data if only unread is set
        if args or set(values.keys()) != {'unread'}:
            super().write(notes, values, *args)
        else:
            # Check access write and clean cache
            ids, field_names, on_write, trigger_eligibles, *args = (
                cls._before_write(notes, values))
            cls.set_unread(notes, 'unread', values['unread'])
            cls._after_write(
                ids, field_names, on_write, trigger_eligibles)


class NoteRead(ModelSQL):
    __name__ = 'ir.note.read'
    note = fields.Many2One('ir.note', 'Note', required=True,
        ondelete='CASCADE')
    user = fields.Many2One('res.user', 'User', required=True,
        ondelete='CASCADE')


class NoteCopyMixin(
        resource_copy('ir.note', 'notes', lazy_gettext('ir.msg_notes'))):
    pass
