# -*- coding: utf-8 -*-
# Copyright 2016 SYLEAM
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import datetime
import dateutil
import time
from collections import defaultdict
from openerp import models, api, fields
from openerp.tools.safe_eval import safe_eval


class OAuthProviderScope(models.Model):
    _name = 'oauth.provider.scope'
    _description = 'OAuth Provider Scope'

    name = fields.Char(
        required=True, translate=True,
        help='Name of the scope, displayed to the user.')
    code = fields.Char(
        required=True, help='Code of the scope, used in OAuth requests.')
    description = fields.Text(
        required=True, translate=True,
        help='Description of the scope, displayed to the user.')
    model_id = fields.Many2one(
        comodel_name='ir.model', string='Model', required=True,
        help='Model allowed to be accessed by this scope.')
    model = fields.Char(
        related='model_id.model', string='Model Name', readonly=True,
        help='Name of the model allowed to be accessed by this scope.')
    filter_id = fields.Many2one(
        comodel_name='ir.filters', string='Filter',
        domain="[('model_id', '=', model)]",
        help='Filter applied to retrieve records allowed by this scope.')
    field_ids = fields.Many2many(
        comodel_name='ir.model.fields', string='Fields',
        domain="[('model_id', '=', model_id)]",
        help='Fields allowed by this scope.')

    _sql_constraints = [
        ('code_unique', 'UNIQUE (code)',
         'The code of the scopes must be unique !'),
    ]

    @api.model
    def _get_ir_filter_eval_context(self):
        """ Returns the base eval context for ir.filter domains evaluation """
        return {
            'datetime': datetime,
            'dateutil': dateutil,
            'time': time,
            'uid': self.env.uid,
            'user': self.env.user,
        }

    @api.multi
    def get_data_for_model(self, model, res_id=None, all_scopes_match=False):
        """ Return the data matching the scopes from the requested model """
        data = defaultdict(dict)
        eval_context = self._get_ir_filter_eval_context()
        all_scopes_records = self.env[model]
        for scope in self.filtered(lambda record: record.model == model):
            # Retrieve the scope's domain
            filter_domain = [(1, '=', 1)]
            if scope.filter_id:
                filter_domain = safe_eval(
                    scope.filter_id.sudo().domain, eval_context)
            if res_id is not None:
                filter_domain.append(('id', '=', res_id))

            # Retrieve data of the matching records, depending on the scope's
            # fields
            records = self.env[model].search(filter_domain)
            for record_data in records.read(scope.field_ids.mapped('name')):
                for field, value in record_data.items():
                    if isinstance(value, tuple):
                        # Return only the name for a many2one
                        data[record_data['id']][field] = value[1]
                    else:
                        data[record_data['id']][field] = value

            # Keep a list of records that match all scopes
            if not all_scopes_records:
                all_scopes_records = records
            else:
                all_scopes_records &= records

        # If all scopes are required to match, filter the results to keep only
        # those mathing all scopes
        if all_scopes_match:
            data = dict(filter(
                lambda record_data: record_data[0] in all_scopes_records.ids,
                data.items()))

        # If a single record was requested, return only data coming from this
        # record
        # Return an empty dictionnary if this record didn't recieve data to
        # return
        if res_id is not None:
            data = data.get(res_id, {})

        return data