# -*- coding: utf-8 -*-
# © 2015-2016 Matthieu Dietrich (Camptocamp SA)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import logging
import datetime
from openerp import api, fields, models
from dateutil.relativedelta import relativedelta

from openerp.addons.server_environment import serv_config

_logger = logging.getLogger(__name__)


class FetchmailServer(models.Model):
    """Incoming POP/IMAP mail server account"""
    _inherit = 'fetchmail.server'

    cleanup_days = fields.Integer(
        compute='_get_cleanup_conf',
        string='Expiration days',
        help="Number of days before marking an e-mail as read")

    cleanup_folder = fields.Char(
        compute='_get_cleanup_conf',
        string='Expiration folder',
        help="Folder where an e-mail marked as read will be moved.")

    purge_days = fields.Integer(
        compute='_get_cleanup_conf',
        string='Deletion days',
        help="Number of days before removing an e-mail")

    @api.multi
    def _get_cleanup_conf(self):
        """
        Return configuration
        """
        for fetchmail in self:
            global_section_name = 'incoming_mail'

            # default vals
            config_vals = {'cleanup_days': False,
                           'purge_days': False,
                           'cleanup_folder': False}
            if serv_config.has_section(global_section_name):
                config_vals.update(serv_config.items(global_section_name))

            custom_section_name = '.'.join((global_section_name,
                                            fetchmail.name))
            if serv_config.has_section(custom_section_name):
                config_vals.update(serv_config.items(custom_section_name))

            # convert string values to integer
            if config_vals['cleanup_days']:
                config_vals['cleanup_days'] = int(config_vals['cleanup_days'])
            if config_vals['purge_days']:
                config_vals['purge_days'] = int(config_vals['purge_days'])

            for field in ['cleanup_days', 'purge_days', 'cleanup_folder']:
                fetchmail[field] = config_vals[field]

    def _cleanup_fetchmail_server(self, server, imap_server):
        count, failed = 0, 0
        expiration_date = datetime.date.today()
        expiration_date -= relativedelta(days=server.cleanup_days)
        search_text = expiration_date.strftime('(UNSEEN BEFORE %d-%b-%Y)')
        imap_server.select()
        result, data = imap_server.search(None, search_text)
        for num in data[0].split():
            try:
                # Mark message as read
                imap_server.store(num, '+FLAGS', '\\Seen')
                if server.cleanup_folder:
                    # To move a message, you have to COPY
                    # then DELETE the message
                    result = imap_server.copy(num, server.cleanup_folder)
                    if result[0] == 'OK':
                        imap_server.store(num, '+FLAGS', '\\Deleted')
            except Exception:
                _logger.exception('Failed to cleanup mail from %s server %s.',
                                  server.type, server.name)
                failed += 1
            count += 1
        _logger.info("Marked %d email(s) as read on %s server %s; "
                     "%d succeeded, %d failed.", count, server.type,
                     server.name, (count - failed), failed)

    def _purge_fetchmail_server(self, server, imap_server):
        # Purging e-mails older than the purge date, if available
        count, failed = 0, 0
        purge_date = datetime.date.today()
        purge_date -= relativedelta(days=server.purge_days)
        search_text = purge_date.strftime('(BEFORE %d-%b-%Y)')
        imap_server.select()
        result, data = imap_server.search(None, search_text)
        for num in data[0].split():
            try:
                # Delete message
                imap_server.store(num, '+FLAGS', '\\Deleted')
            except Exception:
                _logger.exception('Failed to remove mail from %s server %s.',
                                  server.type, server.name)
                failed += 1
            count += 1
        _logger.info("Removed %d email(s) on %s server %s; "
                     "%d succeeded, %d failed.", count, server.type,
                     server.name, (count - failed), failed)

    @api.multi
    def fetch_mail(self):
        """ Called before the fetch, in order to clean up
             right before retrieving emails. """
        context = self.env.context.copy()
        context['fetchmail_cron_running'] = True
        for server in self:
            _logger.info('start cleaning up emails on %s server %s',
                         server.type, server.name)
            context.update({'fetchmail_server_id': server.id,
                            'server_type': server.type})
            imap_server = False
            if server.type == 'imap':
                try:
                    imap_server = server.connect()
                    if server.cleanup_days > 0:
                        self._cleanup_fetchmail_server(server, imap_server)
                    if server.purge_days > 0:
                        self._purge_fetchmail_server(server, imap_server)
                    # Do the final cleanup: delete all messages
                    # flagged as deleted
                    imap_server.expunge()
                except Exception:
                    _logger.exception("General failure when trying to cleanup "
                                      "mail from %s server %s.",
                                      server.type, server.name)
                finally:
                    if imap_server:
                        imap_server.close()
                        imap_server.logout()
        return super(FetchmailServer, self).fetch_mail()