# -*- coding: utf-8 -*-
# Copyright 2018 JDG <www.yunside.com>
# Created by LLH <lianghua.liu@yunside.com> at 2020/3/6

import logging
import csv
import datetime
import io
import itertools
import psycopg2
import operator
import os
import re
import base64
import ast

_logger = logging.getLogger(__name__)

from odoo import api, models, fields, SUPERUSER_ID
from odoo.exceptions import ValidationError, Warning
from odoo.tools.translate import _
from odoo.tools.mimetypes import guess_mimetype
from odoo.tools.misc import ustr
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT

from odoo.osv import expression

from core.middleware import utils

try:
    from cStringIO import StringIO
except ImportError:
    from StringIO import StringIO

try:
    import xlrd

    try:
        from xlrd import xlsx
    except ImportError:
        xlsx = None
except ImportError:
    xlrd = xlsx = None

try:
    import odf_ods_reader
except ImportError:
    odf_ods_reader = None

FILE_TYPE_DICT = {
    'text/csv': ('csv', True, None),
    'application/vnd.ms-excel': ('xls', xlrd, 'xlrd'),
    'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': ('xlsx', xlsx, 'xlrd >= 0.8'),
    'application/vnd.oasis.opendocument.spreadsheet': ('ods', odf_ods_reader, 'odfpy')
}

EXTENSIONS = {
    '.' + ext: handler
    for mime, (ext, handler, req) in FILE_TYPE_DICT.iteritems()
}


class JdBaseWizardNewDataImportLine(models.TransientModel):
    _name = 'jd.base.wizard.new.data.import.line'
    _description = u'新增期初数据导入'

    import_line_id = fields.Many2one('jd.data.import.line', string=u'关联数据导入明细')
    sequence = fields.Integer(string=u'导入顺序')
    biz_type = fields.Char(string=u'单据类型')
    file_qty = fields.Integer(string=u'文件数量', compute='_get_file_qty')
    btn_load_file = fields.Boolean(string=u'验证文件')
    line_ids = fields.One2many('jd.base.wizard.new.data.import.line.line', 'parent_id',
                               string=u'文件明细')

    @api.model
    def default_get(self, fields):
        res = super(JdBaseWizardNewDataImportLine, self).default_get(fields)
        import_line_id = self.env.context.get('import_line_id', None)
        sequence = self.env.context.get('sequence', None)
        biz_type = self.env.context.get('biz_type', None)
        res.update({
            'import_line_id': import_line_id,
            'sequence': sequence,
            'biz_type': biz_type
        })
        return res

    @api.depends('line_ids')
    def _get_file_qty(self):
        """
        统计文件数量
        :return:
        """
        for item in self:
            item.file_qty = len(item.line_ids)

    @api.onchange('btn_load_file')
    @api.multi
    def onchange_btn_load_file(self):
        """
        验证文件
        遍历文件明细，执行文件验证，将验证结果反写回来，失败的将失败原因文本记录
        :return:
        """
        for item in self:
            for line in item.line_ids:
                try:
                    line.load_file()
                    line.load_result = 'success'
                except Exception as e:
                    if hasattr(e, 'message') and e.message:
                        msg = e.message
                    elif hasattr(e, 'name') and e.name:
                        msg = e.name
                    else:
                        msg = u'文件验证失败'
                    line.load_result = 'fail'
                    line.load_result_text = msg

    @api.multi
    def do_confirm(self):
        """
        执行导入
        1、校验明细行中所有的文件都已通过验证
        2、遍历文件明细，执行文件导入，记录导入结果
        3、创建一条导入历史，并根据记录的导入结果创建导入历史明细
        :return:
        """
        import_line_model = self.env['jd.data.import.line']
        for item in self:
            # 校验明细行中所有文件都已通过验证
            if any(line.load_result != 'success' for line in item.line_ids):
                raise ValidationError(u'存在文件未验证成功，请点击验证文件')

            # ==========================执行文件导入，返回导入结果=============================
            # 获取导入方法
            import_func = getattr(import_line_model, utils.G_PARAMS['data_import_biz_type_define_map'][item.import_line_id.biz_type])
            # 声明创建导入历史与导入历史明细需要的变量，获取导入结果
            success_count, fail_count, pass_count = 0, 0, 0
            line_list = []
            for line in item.line_ids:
                try:
                    data = line.load_file()
                    result = import_func(data)
                    success_count += result['success_count'] if result.get('success_count') else 0
                    fail_count += result['fail_count'] if result.get('fail_count') else 0
                    pass_count += result['pass_count'] if result.get('pass_count') else 0
                    line_list.append((0, 0, {'import_file': line.import_file,
                                             'file_name': line.file_name,
                                             'result_code': str(result)}))
                except Exception as e:
                    msg = u'文件导入失败，请联系管理员'
                    if hasattr(e, 'message') and e.message:
                        msg = e.message
                    elif hasattr(e, 'name') and e.name:
                        msg = e.name
                    line_list.append((0, 0, {'import_file': line.import_file,
                                             'file_name': line.file_name,
                                             'error_code': msg}))
            # 创建导入历史与导入历史明细
            result_code = str({'success_count': success_count,
                               'fail_count': fail_count,
                               'pass_count': pass_count,
                               'file_count': item.file_qty})
            self.env['jd.data.import.line.history'].jdg_sudo().create({'line_id': item.import_line_id.id,
                                                                       'result_code': result_code,
                                                                       'time_import': fields.Datetime.now(),
                                                                       'uid_import': self.env.user.id,
                                                                       'line_ids': line_list})


class JdBaseWizardNewDataImportLineLine(models.TransientModel):
    _name = 'jd.base.wizard.new.data.import.line.line'
    _description = u'新增期初数据导入文件明细'

    parent_id = fields.Many2one('jd.base.wizard.new.data.import.line', string=u'新增期初数据导入',
                                ondelete='cascade')
    import_file = fields.Binary(string=u'导入文件', attachment=True, required=True)
    file_name = fields.Char(string=u'文件名称')
    load_result = fields.Selection([('to_load', u'等待验证'),
                                    ('fail', u'验证失败'),
                                    ('success', u'验证成功')], string=u'验证结果', default='to_load')
    load_result_text = fields.Text(string=u'验证结果')

    @api.onchange('import_file')
    def onchange_import_file(self):
        """
        文件发生改变时重置验证结果
        :return:
        """
        for item in self:
            item.load_result = 'to_load'

    @api.multi
    def load_file(self):
        """
        验证文件，校验excel文件格式
        TODO:校验Excel中每个cell数据的合理性（并与字段对应，自动匹配）
        :return:
        """
        if not self.import_file:
            raise ValidationError('存在明细行获取文件失败，请检查。')
        rows = self._read_file()
        data = [
            list(row) for row in rows
            if any(row)
        ]
        return data

    @api.multi
    def _read_file(self):
        """
        读取文件
        通过其mimetype或者文件类型，调用相应的具体读取文件方法，是一个读取文件的统一入口
        :return:
        """
        # 从文件内容guess到mimetype
        # TODO：存在格式报错
        # mimetype = guess_mimetype(self.import_file)
        # (file_extension, handler, req) = FILE_TYPE_DICT.get(mimetype, (None, None, None))
        # if handler:
        #     try:
        #         return getattr(self, '_read_' + file_extension)()
        #     except Exception:
        #         _logger.warn("读取文件失败，不能识别的通过文件内容guess到的mimetype")

        # 从文件名获取扩展类型
        if self.file_name:
            p, ext = os.path.splitext(self.file_name)
            if ext in EXTENSIONS:
                try:
                    return getattr(self, '_read_' + ext[1:])()
                except Exception:
                    _logger.warn("读取文件失败，不能识别通过文件名获取到的文件扩展类型")

        # if req:
        #     raise ImportError(_("不能加载 \"{extension}\" 文件: 需要依赖 \"{modname}\"，请联系管理员").format(
        #         extension=file_extension, modname=req))

        raise ValueError(_("读取文件失败"))

    @api.multi
    def _read_xls(self):
        """
        调用xlrd库读取Excel文件内容
        :return:
        """
        file_binary = base64.b64decode(self.import_file)
        book = xlrd.open_workbook(file_contents=file_binary)
        return self._read_xls_book(book)

    # XLS扩展文件与XLSX文件都执行define：_read_xls()
    _read_xlsx = _read_xls

    @api.model
    def _read_xls_book(self, book):
        """
        获取sheet对象，循环读取每行每个cell的值
        :param book:
        :return:
        """
        sheet = book.sheet_by_index(0)
        for row in itertools.imap(sheet.row, range(sheet.nrows)):
            values = []
            for cell in row:
                if cell.ctype is xlrd.XL_CELL_NUMBER:
                    is_float = cell.value % 1 != 0.0
                    values.append(
                        unicode(cell.value)
                        if is_float
                        else unicode(int(cell.value))
                    )
                elif cell.ctype is xlrd.XL_CELL_DATE:
                    is_datetime = cell.value % 1 != 0.0
                    dt = datetime.datetime(*xlrd.xldate.xldate_as_tuple(cell.value, book.datemode))
                    values.append(
                        dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
                        if is_datetime
                        else dt.strftime(DEFAULT_SERVER_DATE_FORMAT)
                    )
                elif cell.ctype is xlrd.XL_CELL_BOOLEAN:
                    values.append(u'True' if cell.value else u'False')
                elif cell.ctype is xlrd.XL_CELL_ERROR:
                    raise ValueError(
                        _("读取Excel时发现错误的单元格: %s") %
                        xlrd.error_text_from_code.get(
                            cell.value, "不能识别的错误代码 %s" % cell.value)
                    )
                else:
                    values.append(cell.value)
            if any(x for x in values if x.strip()):
                yield values
