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

import logging

from odoo import fields, models, SUPERUSER_ID
from odoo import api, exceptions
from odoo.exceptions import ValidationError
from core.middleware.trust_rpc import get_trust_rpc
from odoo.tools import config
import simplejson
import re
from tpl_engine import TPL_Engine
from odoo.addons.base.ir.ir_ui_menu import IrUiMenu

_logger = logging.getLogger(__name__)


class JDTableReport(models.Model):
    _name = 'jd.table.report'
    _description = u'报表定义'

    number = fields.Char(u'编码', index=True, required=True)
    name = fields.Char(u'名称', index=True, required=True)
    state = fields.Selection([('draft', u'草稿'), ('confirm', u'发布')], u'状态', default='draft', index=True)
    cell_list = fields.One2many('jd.table.report.cell', 'report_id', string=u'元素定义')
    datasource_id = fields.Many2one('jd.table.datasource', required=True, string=u'数据源', domain=[('type', '=', 'report')])
    access_ids = fields.One2many('jd.table.report.access', 'parent_id', string=u'权限定义')
    menu_ids = fields.One2many('jd.table.report.menu', 'parent_id', string=u'菜单')
    # 表头样式的存放 给grapejs使用
    gjshtml = fields.Text(string=u'gjshtml')
    gjscss = fields.Text(string=u'gjscss')
    gjscomponents = fields.Text(string=u'gjscomponents')
    gjsstyles = fields.Text(string=u'gjsstyles')
    gjsassets = fields.Text(string=u'gjsassets')

    # 前端定义 例如第几列固定
    fixed_column = fields.Integer(string=u'列固定')
    sum_js = fields.Text(string=u'合计行js')
    export_filename = fields.Char(string=u'导出文件名')

    _sql_constraints = [
        ('number_uniq', 'unique (number)', u'编号不能重复！')
    ]

    # @api.model_cr
    # def _register_hook(self):
    #     """
    #     服务启动并将所有的module加载入registry后，会调用该方法。
    #     :return:
    #     """
    #     recs = self.search([('datasource_id', '!=', False)])
    #     for rec in recs:
    #         if not rec.menu_ids:
    #             continue
    #         for menu in rec.menu_ids:
    #             if menu.type == 'edit' and menu.external_id:
    #                 rec.update_menu_action(rec, menu.external_id)
    #             else:
    #                 continue

    def create_action(self, datasource, menu):
        model = datasource.search_model
        vals = {
            'name': menu.name,
            'res_model': model.model,
            'view_mode': 'tree,form',
            'view_type': 'form',
            'target': 'new',
            'view_id': datasource.search_view.id or None,  # 打开对应的过滤条件视图。
            'context': simplejson.dumps({'report_id': self.id, 'datasource_id': datasource.id})
        }
        action_id = self.env['ir.actions.act_window'].create(vals)
        return action_id

    def update_menu_action(self, rec, external_id):
        model_data_rec = self.env['ir.model.data'].search([('module', '=', external_id.split('.')[0]),
                                                           ('name', '=', external_id.split('.')[1])], limit=1)
        if model_data_rec:
            menu = model_data_rec.reference
            menu = self.env[menu.split(',')[0]].browse(int(menu.split(',')[1]))
            if isinstance(menu, IrUiMenu):
                action_id = self.create_action(rec.datasource_id, menu)
                menu.write({
                    'action': 'ir.actions.act_window,%d' % (action_id,)
                })
            else:
                pass

    def contain_chinese(self, string):
        """
        检查整个字符串是否包含中文
        :param string: 需要检查的字符串
        :return: bool
        """
        RE = re.compile(u'[\u4e00-\u9fa5]', re.UNICODE)
        match = re.search(RE, string)
        if match is None:
            return False
        else:
            return True

    @api.multi
    @api.constrains('number')
    def check_number(self):
        """
        编码不能包含中文
        :return: 
        """
        for item in self:
            if item.contain_chinese(item.number):
                raise ValidationError(u'编码不能包含中文')

    @api.multi
    def generate_xml(self):
        """
        基于当前记录自动生成data.xml文件
        :return:
        """
        for item in self:
            tpl_engine = TPL_Engine()
            external_module_name = config.get('external_module_name', 'jd_report')
            des_path = config.get('des_path', '.')
            des_path = des_path + '/' + item.number
            import os
            is_exist = os.path.exists(des_path)
            if not is_exist:
                os.makedirs(des_path)
            data = {
                'external_module_name': external_module_name,
                'number': item.number,
                'name': item.name,
                'state': item.state,
                'gjshtml': item.gjshtml,
                'gjscss': item.gjscss,
                'gjscomponents': item.gjscomponents,
                'gjsstyles': item.gjsstyles,
                'gjsassets': item.gjsassets,
                'fixed_column': item.fixed_column,
                'sum_js': item.sum_js,
                'external_id': 'sql_' + item.datasource_id.number,
                'datasource_id': item.datasource_id.id}
            report_str = tpl_engine.render('tpl_report.xml', data)
            # report_str = report_str.replace("<=", "&lt;=")
            # report_str = report_str.replace(">=", "&gt;=")
            # 写入表头
            sql_filename = '%s/%s_report.xml' % (des_path, item.number)
            with open(sql_filename, mode='w+') as f:
                f.write(report_str)
            external_rec = self.env['ir.model.data'].search([('name', '=', 'report_%s' % item.number), ('module', '=', config.get('external_module_name', 'jd_report'))])
            if external_rec:
                external_rec.unlink()
            self.env['ir.model.data'].create({
                'name': 'report_%s' % item.number,
                'model': 'jd.table.report',
                'module': config.get('external_module_name', 'jd_report'),
                'res_id': item.id,
            })
            # 写入单元格明细
            data_list = []
            for index, cell in enumerate(item.cell_list):
                data_list.append({
                    'external_module_name': external_module_name,
                    'index': index,
                    'external_id': 'report_%s' % item.number ,
                    # 'report_id': cell.report_id.id,
                    'report_number': cell.report_id.number,
                    'row_index': cell.row_index,
                    'col_index': cell.col_index,
                    'style': cell.style,
                    'value': cell.value,
                    'merge_x1': cell.merge_x1,
                    'merge_y1': cell.merge_y1,
                    'merge_x2': cell.merge_x2,
                    'merge_y2': cell.merge_y2,
                })
                external_rec = self.env['ir.model.data'].search([('name', '=', 'report_cell_%s_%d' % (item.number, index)), (
                'module', '=', config.get('external_module_name', 'jd_report'))])
                if external_rec:
                    external_rec.unlink()
                self.env['ir.model.data'].create({
                    'name': 'report_cell_%s_%d' % (item.number, index),
                    'model': 'jd.table.report.cell',
                    'module': config.get('external_module_name', 'jd_report'),
                    'res_id': cell.id,
                })
            data_dic = {'data': data_list}
            table_cell_str = tpl_engine.render('tpl_report_cell.xml', data_dic)
            report_cell_filename = '%s/%s_report_cell.xml' % (des_path, item.number)
            with open(report_cell_filename, mode='w') as f:
                f.write(table_cell_str)
            # 写入权限定义明细
            data_access_list = []
            for index, access in enumerate(item.access_ids):
                data_access_list.append({
                    'external_module_name': external_module_name,
                    'index': index,
                    'external_id': 'report_%s' % item.number,
                    'report_number': access.parent_id.number,
                    'group_id': access.group_id.id or "",
                    'company_ids': [(6, 0, access.company_ids.ids)],
                    'note': access.note,
                })
                external_rec = self.env['ir.model.data'].search([('name', '=', 'report_access_%s_%d' % (item.number, index)), (
                'module', '=', config.get('external_module_name', 'jd_report'))])
                if external_rec:
                    external_rec.unlink()
                self.env['ir.model.data'].create({
                    'name': 'report_access_%s_%d' % (item.number, index),
                    'model': 'jd.table.report.access',
                    'module': config.get('external_module_name', 'jd_report'),
                    'res_id': access.id,
                })
            data_access_dic = {'data': data_access_list}
            table_access_str = tpl_engine.render('tpl_report_access.xml', data_access_dic)
            report_access_filename = '%s/%s_report_access.xml' % (des_path, item.number)
            with open(report_access_filename, mode='w') as f:
                f.write(table_access_str)
            # 写入发布菜单
            data_menu_list = []
            for index, menu in enumerate(item.menu_ids):
                data_menu_list.append({
                    'external_module_name': external_module_name,
                    'index': index,
                    'external_id': 'report_%s' % item.number,
                    'report_number': menu.parent_id.number,
                    'type': menu.type or "",
                    'external_menu_id': menu.external_id,
                })
                external_rec = self.env['ir.model.data'].search([('name', '=', 'report_menu_%s_%d' % (item.number, index)), (
                'module', '=', config.get('external_module_name', 'jd_report'))])
                if external_rec:
                    external_rec.unlink()
                self.env['ir.model.data'].create({
                    'name': 'report_menu_%s_%d' % (item.number, index),
                    'model': 'jd.table.report.menu',
                    'module': config.get('external_module_name', 'jd_report'),
                    'res_id': menu.id,
                })
            data_menu_dic = {'data': data_menu_list}
            table_menu_str = tpl_engine.render('tpl_report_menu.xml', data_menu_dic)
            report_menu_filename = '%s/%s_report_menu.xml' % (des_path, item.number)
            with open(report_menu_filename, mode='w') as f:
                f.write(table_menu_str)

    @api.multi
    def publish_report(self):
        return {
            'name': '发布报表',
            'type': 'ir.actions.act_window',
            'model': 'ir.actions.act_window',
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'jd.wizard.create.report.menu',
            'target': 'new',
            'view_id': False,
            # 'context': {'edit': True}
        }

    @api.multi
    def unlink(self):
        '''
        删除对应的菜单
        :return:
        '''
        for record in self:
            if record.menu_ids and len(record.menu_ids) > 0:
                record.menu_ids.unlink()
        return super(JDTableReport, self).unlink()

    def action_design(self):
        '''
        打开报表设计器
        :return:
        '''
        return {
            'type': 'ir.actions.act_url',
            'url': '/v1/jd_report/editor?id=' + str(self.id),
            'target': 'new',
        }

    def header_design(self):
        '''
        打开表头设计
        :return:
        '''
        return {
            'type': 'ir.actions.act_url',
            'url': '/v1/jd_report/header_editor?id=' + str(self.id),
            'target': 'new',
        }

    def action_preview(self):
        '''
        预览
        :return:
        '''
        report = self.env['jd.table.report'].browse(self.id)
        if report.datasource_id:
            model = report.datasource_id.search_model
            vals = {
                'name': u'报表预览',
                'type': 'ir.actions.act_window',
                'model': 'ir.actions.act_window',
                'res_model': model.model,
                'view_mode': 'form',
                'view_type': 'form',
                'target': 'new',
                'view_id': report.datasource_id.search_view.id or None,  # 打开对应的过滤条件视图。
                'context': {'report_id': report.id, 'datasource_id': report.datasource_id.id}
            }
            return vals

    def get_dict_data(self, search_wizard_id, search_wizard_model):
        '''
        根据过滤条件，拼接SQL，并查询报表数据
        :param search_wizard_id:
        :param search_wizard_model:
        :return: data_list, param_value_dict
        '''
        wizard_id = search_wizard_id
        wizard_model = search_wizard_model
        search_field_value = {}
        search_content = {}
        if wizard_model and wizard_id:
            # 获取查询参数值
            wizard = self.env[wizard_model].browse(int(wizard_id))
            if wizard and wizard.id:
                for f_name in wizard._fields:
                    if f_name.startswith("x_"):
                        params_name = "@" + f_name[2:]
                        val = wizard[f_name]
                        search_content.update({params_name: val})
                        # 判断是枚举
                        if wizard._fields[f_name].type == 'selection':
                            if dict(wizard._fields[f_name].selection).get(wizard[f_name]):
                                selection_value = dict(wizard._fields[f_name].selection).get(wizard[f_name])
                            else:
                                selection_value = '未选择'
                            search_content.update({
                                params_name: selection_value
                            })
                        # 判断是多选枚举
                        elif wizard._fields[f_name].type == 'multi_selection':
                            multi_selection_value = []
                            selection_dic = dict(wizard._fields[f_name].selection)
                            multi_selection_key = wizard[f_name].split(',')
                            for key in multi_selection_key:
                                if key != 'false':
                                    multi_selection_value.append(selection_dic.get(str(key)[1:-1]))
                            if len(multi_selection_value) > 0:
                                multi_selection_value = ','.join(multi_selection_value)
                            else:
                                multi_selection_value = '未选择'
                            search_content.update({
                                params_name: multi_selection_value
                            })
                        # 判断是m2o字段
                        elif wizard._fields[f_name].type == 'many2one':
                            m2o_value = "未选择"
                            if len(wizard[f_name]) > 0:
                                if getattr(wizard[f_name], 'name'):
                                    m2o_value = wizard[f_name].name
                                elif getattr(wizard[f_name], 'number'):
                                    m2o_value = wizard[f_name].number
                            search_content.update({
                                params_name: m2o_value
                            })
                        # 判断是m2m字段
                        elif wizard._fields[f_name].type == 'many2many':
                            m2m_value = []
                            if len(wizard[f_name]) > 0:
                                for m2m in wizard[f_name]:
                                    if getattr(m2m, 'name'):
                                        m2m_value.append(m2m.name)
                                    elif getattr(wizard[f_name], 'number'):
                                        m2m_value.append(m2m.number)
                            else:
                                m2m_value = ['未选择']
                            search_content.update({
                                params_name: ",".join(m2m_value)
                            })
                        if hasattr(val, 'ids'):
                            val = ",".join([str(x) for x in val.ids])
                        elif hasattr(val, 'id'):
                            val = val.id
                        search_field_value.update({
                            params_name: str(val)
                        })
        print search_content
        print search_field_value
        ds = self.datasource_id
        sql = ds.sql
        # 替换查询参数为对应的值。
        for key in search_field_value.keys():
            search_val = search_field_value[key]
            if search_val and search_val != 'False':
                sql = sql.replace(key + "_null", '1')  # 空值控制
                sql = sql.replace(key, search_val)
            else:
                sql = sql.replace(key + "_null", 'null')  # 空值控制
                sql = sql.replace(key, '0')
        # 判断sql中是否存在if语句
        # 查找所有的[if][end if]语句块。 取出判断条件为真的语句进行拼接
        import re
        pattern = re.compile(r'\[if.*?\[end if\]')
        result = pattern.findall(sql)
        for r in result:
            matcher = re.search(r'\[if(.*)then\]', r)
            try:
                if eval(matcher.group(1)):
                    matcher = re.search(r'then\](.*)\[end if\]', r)
                    sql = sql.replace(r, matcher.group(1), 1)
                else:
                    sql = sql.replace(r, " ", 1)
            except Exception as e:
                raise e
        _logger.debug('sql is %s ' % sql)
        # 判断在哪里进行sql语句的执行
        current_data_node_id = self.env['ir.config_parameter'].sudo().get_param("jadedragon.microapp.data.node.id")
        des_data_node_id = ds.data_node_id.id
        # 如果数据节点和当前数据节点一致，直接本地执行sql
        if int(current_data_node_id) == des_data_node_id:
            self.env.cr.execute(sql)
            data_list = self.env.cr.dictfetchall()
            return data_list, search_content
        # 否则使用trustRpc获取远程数据
        else:
            try:
                remote_odoo = get_trust_rpc(des_data_node_id)
                # 调业务层执行sql的模型和方法 模型名为report.execute.sql， 方法名为execute_sql
                data_list = remote_odoo.env['report.execute.sql'].execute_sql(sql)
                _logger.debug('data_list is %s' % data_list)
                return data_list, search_content
            except Exception as e:
                _logger.debug(e)
                _logger.debug('error occured....')
                return [], {}

    @api.multi
    def write(self, vals):
        res = super(JDTableReport, self).write(vals)
        if 'access_ids' in vals:
            self.reset_groups()
        return res

    @api.multi
    @api.constrains('access_ids')
    def check_unique_group(self):
        """
        权限定义的权限组唯一
        :return:
        """
        for item in self:
            exist_groups = set()
            for line in item.access_ids:
                if line.group_id.id not in exist_groups:
                    exist_groups.add(line.group_id.id)
                else:
                    raise ValidationError(u'权限定义中的权限组只能唯一，请检查【%s】' % line.group_id.name)

    @api.multi
    def reset_groups(self):
        """
        每次变更权限，都重新设置报表的功能权限
        :return:
        """
        self.ensure_one()
        item = self[0]
        for menu in item.menu_ids:
            group_ids = []
            for access in item.access_ids:
                group_ids.extend(access.group_id.group_ids.ids)
            base_groups = [self.env.ref('base.group_erp_manager').id,
                           self.env.ref('base.group_system').id]
            group_ids.extend(base_groups)
            group_val = [(6, 0, group_ids)]
            menu.groups_id = group_val

    def get_groups(self, report_id):
        """
        获取允许该访问该报表的权限组
        :return:
        """
        assert isinstance(report_id, int)
        report = self.browse(report_id)
        assert report.state == 'confirm'
        group_ids = []
        for access in report.access_ids:
            group_ids.extend(access.group_id.group_ids.ids)
        return group_ids

    def get_group_company_ids(self, report_id, group_id):
        """
        获取某报表对某个角色组的可筛选的组织范围
        :param report_id:
        :param group_id:
        :return:
        """
        assert isinstance(report_id, int)
        assert isinstance(group_id, int)
        report = self.browse(report_id)
        assert report.state == 'confirm'
        group_ids = [access.group_id.id for access in report.access_ids]
        if group_id not in group_ids:
            return []
        else:
            rec = report.access_ids.filtered(lambda r: r.group_id.id == group_id)
            if rec:
                company_ids = rec.company_ids.ids
            else:
                company_ids = []
            return company_ids

    def get_uid_company_ids(self, report_id, uid):
        """
        获取某用户对某报表的可筛选的组织范围
        可筛选的组织范围 = 用户允许访问的组织 和 报表配置的所有组织范围的 交集
        :param report_id:
        :param uid:
        :return:
        """
        assert isinstance(report_id, int)
        assert isinstance(uid, int)
        report = self.browse(report_id)
        assert report.state == 'confirm'
        company_ids = self.env['res.company'].sudo().search([]).ids
        cr = self.env.cr
        if uid == SUPERUSER_ID:
            return company_ids
        else:
            # 获取用户的权限组
            sql = 'select * from res_groups_users_rel where uid=%s' % uid
            cr.execute(sql)
            group_ids = [res['gid'] for res in cr.dictfetchall()]
            # 获取这些权限组对该报表可筛选的组织范围
            report_company_ids = []
            for group_id in group_ids:
                report_company_ids.extend(self.get_group_company_ids(report_id=report_id, group_id=group_id))
            # 获取用户可访问的组织范围
            user_company_ids = self.env['res.users'].browse(uid).company_ids.ids
            # 用户对该报表可访问的组织范围为 用户的组织范围和报表可筛选的组织范围的交接
            company_ids = set(user_company_ids).intersection(set(report_company_ids))
            return list(company_ids)


class JDTableReportCell(models.Model):
    _name = 'jd.table.report.cell'
    _description = u'报表元素定义'

    report_id = fields.Many2one('jd.table.report', String=u'报表', index=True, ondelete='cascade')
    row_index = fields.Integer(u'行标', required=True)
    col_index = fields.Integer(u'列标', required=True)
    style = fields.Text(u'样式')
    value = fields.Char(u'值')
    merge_x1 = fields.Integer(u'合并开始列')
    merge_y1 = fields.Integer(u'合并开始行')
    merge_x2 = fields.Integer(u'合并结束列')
    merge_y2 = fields.Integer(u'合并结束行')


class JdTableReportAccess(models.Model):
    _name = 'jd.table.report.access'
    _description = u'报表权限定义'

    parent_id = fields.Many2one('jd.table.report', string=u'报表', index=True, ondelete='cascade')
    group_id = fields.Many2one('jd_base.department', string=u'角色组', required=True)
    company_ids = fields.Many2many('res.company', string=u'允许组织')
    refuse_company_ids = fields.Many2many('res.company', string=u'拒绝组织')
    note = fields.Text(string=u'备注')


class JdTableReportMenu(models.Model):
    _name = 'jd.table.report.menu'
    _description = u'报表已发布菜单'

    parent_id = fields.Many2one('jd.table.report', string=u'报表定义', index=True, ondelete='cascade')
    menu_id = fields.Many2one('ir.ui.menu', string=u'菜单项', readonly=True)
    type = fields.Selection([('edit', u'发布到已有菜单'),
                             ('create', u'新建菜单')], string=u'发布类型', readonly=True)
    external_id = fields.Char(string=u'外部ID', readonly=True)
