# -*- encoding: utf-8 -*-
# 项目：JDG-
# 模块名称：
# 描述：
# Copyright 2018 JDG <www.yunside.com>
# Created by wxh (xianhuo.weng@yunside.com) at  2019/8/8 - 5:32 PM

from odoo import fields, api, models
import json
from odoo.exceptions import ValidationError

RANGE = [('inner', u'内部'), ('outer', u'外部')]
AUTH_METHOD = [('sms', u'短信'), ('qywx', u'企业微信'), ('email', u'电子邮件'), ('dd', u'钉钉')]


# code_list = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n']


class SignConfig(models.Model):
    _name = 'jd.digital.signature.config'
    _inherit = 'jdg.abstract.base.data.m'
    _description = u'电子签名配置'

    company_id = fields.Many2one('res.company', default=False)
    name = fields.Char(required=False)

    model_id = fields.Many2one('ir.model', string=u'单据类型', required=True, index=True, help=u'需要进行电子签名的业务单据')
    range = fields.Selection(RANGE, string=u'使用范围', default='inner', required=True, help=u'描述是集团内的业务单据还是集团外的业务单据')
    scene = fields.Char(string=u'使用场景', help=u'业务单据的实际场景描述')
    is_certificate = fields.Boolean(string=u'是否带证书', help=u'不需要带证书的业务单据可本地合成签名后的单据')

    is_seal = fields.Boolean(string=u'是否带印章', required=True, default=False)
    is_seal_certificate = fields.Boolean(string=u'印章是否带证书', default=False)
    # 只可选启用的章
    seal_line_id = fields.Many2one('jd.digital.signature.seal.line', string=u'印章')

    line_ids = fields.One2many('jd.digital.signature.config.line', 'parent_id', string=u'签字名单配置')

    domain_model_id = fields.Char(string=u'单据类型domain', compute='_get_domain_model_id')
    domain_seal_line_id = fields.Char(string=u'印章domain', compute='_get_domain_seal_line_id')
    role = fields.Char(string=u'签字人', related='line_ids.role')

    @api.multi
    @api.depends('state')
    def get_hide_edit_button(self):
        for item in self:
            item.hide_edit_button = item.state in ('enable', 'discard')

    @api.model
    def create(self, values):
        """
        获取自动编号
        :param values:
        :return:
        """
        if values.get('number', '/') == '/':
            values.update({
                'number': self.env['ir.sequence'].next_by_code(self._name)
            })
        return super(SignConfig, self).create(values)

    # @api.multi
    # def write(self, values):
    #     if 'line_ids' in values:
    #         item = self[0]
    #         for line_value in values['line_ids']:
    #             used_code_set = set()
    #             for line_exist in item.line_ids:
    #                 used_code_set.add(line_exist.code)
    #             code_extra = set(code_list) - used_code_set
    #             # 新增则重新分配编号
    #             if line_value[0] == 0:
    #                 line_value[2].update({'code': code_extra.pop()})
    #     return super(SignConfig, self).write(values)

    @api.multi
    @api.depends('model_id')
    def name_get(self):
        """
        名称显示格式：[XXX]YYY
        """
        result = []
        for item in self:
            name = u'【%s】电子签名配置' % self.env[item.model_id.model]._description
            result.append((item.id, name))
        return result

    @api.multi
    @api.constrains('model_id', 'range')
    def check_model(self):
        """
        重复校验
        :return: 
        """
        for item in self:
            rec = self.sudo().search(
                [('state', '!=', 'discard'), ('model_id', '=', item.model_id.id), ('range', '=', item.range),
                 ('id', '!=', item.id)])
            if rec:
                raise ValidationError(u'已存在非废弃状态的【%s】电子签名配置' % self.env[item.model_id.model]._description)

    @api.multi
    @api.depends('model_id')
    def _get_domain_model_id(self):
        for item in self:
            base_biz_class = self.env['jdg.abstract.base.biz'].__class__
            ids = [0]
            model_recs = self.env['ir.model'].sudo().search(
                [('model', 'not like', 'ir.'), ('model', 'not like', 'wizard.')])
            for rec in model_recs:
                if rec.model in self.env and issubclass(self.env[rec.model].__class__, base_biz_class):
                    ids.append(rec.id)
            item.domain_model_id = json.dumps([('id', 'in', ids)])

    @api.multi
    @api.depends('seal_line_id')
    def _get_domain_seal_line_id(self):
        for item in self:
            ids = self.env['jd.digital.signature.seal.line'].search([('parent_id.state', '=', 'enable')]).ids
            ids.append(0)
            item.domain_seal_line_id = json.dumps([('id', 'in', ids)])

    @api.multi
    def verify_data(self):
        for item in self:

            # 是否存在已启用的数据
            rec = self.sudo().search(
                [('state', '=', 'enable'), ('model_id', '=', item.model_id.id), ('range', '=', item.range)])
            if rec:
                raise ValidationError(u'存在已启用的【%s】电子签名配置，请检查' % self.env[item.model_id.model]._description)

            role_set = set()
            code_set = set()
            seq_set = set()

            for line in item.line_ids:

                if not item.is_certificate and line.is_certificate:
                    raise ValidationError(u'签字人员【%s】已带证书，请勾选单据【是否带证书】' % line.role)

                # 不允许码上签 与 本地签 同时为空
                if not line.is_qr_sign and not line.is_local_sign:
                    raise ValidationError(u'签字人【%s】的签字方式不允许【码上签】【本地签】同时为空' % line.role)

                # 签字人重复校验
                if line.role in role_set:
                    raise ValidationError(u'签字名单配置的签字人重复')
                else:
                    role_set.add(line.role)

                # 简码重复校验
                if line.code in code_set:
                    raise ValidationError(u'签字名单配置的简码重复')
                else:
                    code_set.add(line.code)

                # 签字顺序合法性与重复校验
                if line.seq in seq_set:
                    raise ValidationError(u'签字名单配置的顺序重复')
                else:
                    seq_set.add(line.seq)

    @api.multi
    @api.onchange('model_id')
    def onchange_model(self):
        for item in self:
            item.line_ids = []

    @api.multi
    @api.onchange('is_certificate')
    def onchange_certificate(self):
        for item in self:
            if item.is_certificate is False:
                item.line_ids.write({'is_certificate': False})

    @api.multi
    def do_enable(self):
        for item in self:
            item.verify_data()
        return super(SignConfig, self).do_enable()

    @api.model
    def get_signature_config(self, model, order_range='inner'):
        """
        获取业务单据的签名人员配置列表，并按照签字顺序排序
        :param model:
        :param order_range:
        :return:
        """
        if hasattr(model, '_name'):
            model = model._name
        result = []
        rec = self.sudo().search([('model_id.model', '=', model), ('state', '=', 'enable'), ('range', '=', order_range)], limit=1,
                                 order='write_date desc')
        if rec:
            # 按seq正序排序
            sorted_line_ids = sorted(rec.line_ids, key=lambda line_id: line_id.seq)
            result = map(lambda line_id: {
                'role': line_id.role,
                'code': line_id.code,
                'seq': line_id.seq,
                'is_qr_sign': line_id.is_qr_sign,
                'is_local_sign': line_id.is_local_sign,
                'role_id': line_id.id,
                'is_certificate': line_id.is_certificate,
            }, sorted_line_ids)
        return result


class SignConfigLine(models.Model):
    _name = 'jd.digital.signature.config.line'
    _description = u'签字人员配置'

    parent_id = fields.Many2one('jd.digital.signature.config', string=u'电子签名配置', ondelete='cascade', required=True)

    role = fields.Char(string=u'签字人', required=True, help=u'注意：签字人必须与打印单据上的对应名字一致')
    code = fields.Char(string=u'简码', size=1, required=True, help=u'一位简码，用于标识不同签字人')
    seq = fields.Integer(string=u'签字顺序', required=True, help=u'数字越小表示越靠前签名')
    is_qr_sign = fields.Boolean(string=u'是否码上签', default=False, help=u'是否启用二维码 扫码签字')
    is_local_sign = fields.Boolean(string=u'是否本地签', default=False, help=u'是否启用本地签名 APP内签字')
    is_certificate = fields.Boolean(string=u'是否带证书', required=True, default=False)
    auth_method = fields.Selection(AUTH_METHOD, string=u'鉴权方式', help=u'若为空则表示该签名不需要鉴权，可直接在APP显示签名界面')

    @api.multi
    def write(self, vals):
        # 如果已存在jd.digital.signature.status 签名记录 则 不允许修改code
        for item in self:
            if 'code' in vals:
                res_model = item.parent_id.model_id.model
                status_recs = self.env['jd.digital.signature.status'].sudo().search(
                    [('res_model', '=', res_model), ('role_id', '=', item.id)])
                if status_recs:
                    raise ValidationError(u'单据【%s】已存在签名数据，不允许修改简码' % self.env[res_model]._description)
        return super(SignConfigLine, self).write(vals)

    @api.multi
    @api.depends('parent_id.model_id', 'parent_id.range', 'role')
    def name_get(self):
        """
        名称显示格式：[XXX]YYY
        """
        result = []
        for item in self:
            order_name = self.env[item.parent_id.model_id.model]._description
            range = dict(RANGE).get(item.parent_id.range)
            name = u'【%s】%s-%s' % (item.role, order_name, range)
            result.append((item.id, name))
        return result
