import json
import time
import logging
import traceback
from datetime import datetime, timedelta
from collections import OrderedDict
from . import app
from settings import conf
from apps.doc.models import (
    AFCOCRResult,
    AFCSEOCRResult,
    HILOCRResult,
    HILSEOCRResult,
    AFCComparisonInfo,
    AFCSEComparisonInfo,
    HILComparisonInfo,
    HILSEComparisonInfo,
    Configs,
    HILCompareReport,
    AFCCompareReport,
    AFCSECompareResult,
    AFCCACompareResult,
    HILSECompareResult,
    HILCACompareResult,
)
from apps.doc import consts
from apps.doc.ocr.gcap import gcap
from apps.doc.ocr.cms import cms
from apps.doc.exceptions import GCAPException
from apps.doc.named_enum import RequestTeam, RequestTrigger, ProcessName, ErrorType
from common.tools.comparison import cp

compare_log = logging.getLogger('compare')
log_base = '[Compare]'
empty_str = ''
empty_error_type = 1000


class FakePOS:

    def __init__(self,
                 application_id,
                 first_submmison_date,
                 application_version,
                 customer_type,
                 individual_cus_info,
                 corporate_cus_info,
                 vehicle_info,
                 bank_info):
        self.application_id = application_id
        self.first_submmison_date = first_submmison_date
        self.application_version = application_version
        self.customer_type = customer_type
        self.individual_cus_info = individual_cus_info
        self.corporate_cus_info = corporate_cus_info
        self.vehicle_info = vehicle_info
        self.bank_info = bank_info


def name_check(ocr_res_dict, second_ocr_field, second_compare_list, second_id_num, name):
    id_field = second_compare_list[1][1]
    name_field = second_compare_list[0][1]
    ocr_res_str = ocr_res_dict.get(second_ocr_field)
    if ocr_res_str is not None:
        ocr_res_list = json.loads(ocr_res_str)
        for ocr_res in ocr_res_list:
            ocr_second_id_num = ocr_res.get(id_field)
            if ocr_second_id_num == second_id_num:
                ocr_name = ocr_res.get(name_field)
                if ocr_name == name:
                    return True
    return False


def get_order_dict(src_dict, order_tuple):
    # if consts.SECOND_ID_TYPE_FIELD in src_dict:
    #     if src_dict.get(consts.SECOND_ID_TYPE_FIELD) not in consts.SECOND_ID_TYPE_COMPARE:
    #         src_dict.pop(consts.SECOND_ID_TYPE_FIELD, None)
    #         src_dict.pop(consts.SECOND_ID_FIELD, None)

    order_dict = OrderedDict({})
    for field in order_tuple:
        if field in src_dict:
            order_dict[field] = src_dict[field]
    return order_dict


def do_not_compare(info_dict, compare_list):
    for compare_tuple in compare_list:
        info_dict[compare_tuple[4]] = consts.RESULT_NA
        if compare_tuple[0] in info_dict:
            del info_dict[compare_tuple[0]]


def field_compare(info_dict, ocr_res_dict, ocr_field, compare_list, res_set,
                  has_expiry_date=False, sep_name=None, capital_ignore=False):
    is_find = False
    ocr_res_str = ocr_res_dict.get(ocr_field)
    if ocr_res_str is not None:
        ocr_res_list = json.loads(ocr_res_str)
        # length = len(ocr_res_list)

        # sep营业执照根据法人过滤
        if isinstance(sep_name, str):
            tmp_list = []
            for ocr_res in ocr_res_list:
                ocr_sep_name = ocr_res.get(consts.LEGAL_REP_NAME)
                if isinstance(ocr_sep_name, str) and ocr_sep_name == sep_name:
                    tmp_list.append(ocr_res)
        else:
            tmp_list = ocr_res_list

        length = len(tmp_list)

        # 过期期限特殊处理
        if has_expiry_date:
            expiry_dates = []
            key = compare_list[2][1]
            for ocr_res in tmp_list:
            # for ocr_res in ocr_res_list:
                if ocr_res.get(key):
                    expiry_dates.append(ocr_res.get(key))
        else:
            expiry_dates = []

        for res_idx in range(length-1, -1, -1):
        # for ocr_res in ocr_res_list:
            if is_find:
                break
            ocr_res = tmp_list[res_idx]
            for idx, compare_tuple in enumerate(compare_list):
                input_str = info_dict.get(compare_tuple[0])
                ocr_str = ocr_res.get(compare_tuple[1])
                compare_res, ocr_output = getattr(cp, compare_tuple[2])(
                    input_str, ocr_str, idx, **compare_tuple[3])
                # print('type: {0}, idx: {1}, field: {2}, input: {3}, ocr: {4}, res: {5}, out: {6}'.format(ocr_field, idx, compare_tuple[0], input_str, ocr_str, compare_res, ocr_output))
                if idx == 0 and compare_res in [consts.RESULT_N, consts.RESULT_NA] and length > 1:
                    break
                is_find = True

                # 过期期限特殊处理
                if idx == 2 and has_expiry_date and compare_res == consts.RESULT_NA:
                    for expiry_date in expiry_dates:
                        expiry_date_compare_res, expiry_date_ocr_output = getattr(cp, compare_tuple[2])(
                            input_str, expiry_date, idx, **compare_tuple[3]
                        )
                        if expiry_date_compare_res == consts.RESULT_Y:
                            compare_res = consts.RESULT_Y
                            ocr_output = expiry_date_ocr_output
                            ocr_str = expiry_date
                            break

                info_dict[compare_tuple[4]] = compare_res
                if input_str is not None:
                    if ocr_str is None or ocr_output is None:
                        del info_dict[compare_tuple[0]]
                    else:
                        info_dict[compare_tuple[0]] = ocr_output
                if capital_ignore and idx == 1:
                    pass
                elif capital_ignore and idx == 2 and input_str is None:
                    pass
                else:
                    res_set.add(compare_res)
    if not is_find:
        res_set.add(consts.RESULT_N)
        for compare_tuple in compare_list:
            info_dict[compare_tuple[4]] = consts.RESULT_NA
            if compare_tuple[0] in info_dict:
                del info_dict[compare_tuple[0]]


def usedcar_info_compare(info_dict, ocr_res_dict, ocr_field, compare_list, res_set):
    no_match_vino = []
    is_find = False
    key_right = False
    ocr_res_str = ocr_res_dict.get(ocr_field)
    if ocr_res_str is not None:
        ocr_res_list = json.loads(ocr_res_str)

        # 3、4页/副页去除
        # if ocr_field == consts.MVC_OCR_FIELD:
        tmp_list = []
        for res in ocr_res_list:
            if res.get(compare_list[0][1]) is not None:
                tmp_list.append(res)

        length = len(tmp_list)

        for res_idx in range(length-1, -1, -1):
            if is_find:
                break
            ocr_res = tmp_list[res_idx]
            for idx, compare_tuple in enumerate(compare_list):
                input_str = info_dict.get(compare_tuple[0])
                ocr_str = ocr_res.get(compare_tuple[1])
                compare_res, ocr_output = getattr(cp, compare_tuple[2])(
                    input_str, ocr_str, idx, **compare_tuple[3])
                # print('type: {0}, idx: {1}, field: {2}, input: {3}, ocr: {4}, res: {5}, out: {6}'.format(ocr_field, idx, compare_tuple[0], input_str, ocr_str, compare_res, ocr_output))
                if idx == 0 and compare_res in [consts.RESULT_N, consts.RESULT_NA]:
                    if ocr_str is not None:
                        no_match_vino.append(ocr_str)
                    if length > 1:
                        break
                is_find = True
                if idx == 0 and compare_res == consts.RESULT_Y:
                    key_right = True
                info_dict[compare_tuple[4]] = compare_res
                if input_str is not None:
                    if idx != 0:
                        if ocr_str is None or ocr_output is None:
                            del info_dict[compare_tuple[0]]
                        else:
                            info_dict[compare_tuple[0]] = ocr_output
                res_set.add(compare_res)
    if not is_find:
        res_set.add(consts.RESULT_N)
        for idx, compare_tuple in enumerate(compare_list):
            info_dict[compare_tuple[4]] = consts.RESULT_NA
            if idx == 0:
                continue
            if compare_tuple[0] in info_dict:
                del info_dict[compare_tuple[0]]
    return is_find and key_right, no_match_vino


def get_ca_compare_info(last_obj):
    compare_info = {}
    individual_info_dict = {}
    for individual_info in json.loads(last_obj.individual_cus_info):
        license_dict = {}

        if individual_info['idType'] in consts.CA_FIRST_ID_FIELD_MAPPING:
            license_en, field_list = consts.CA_FIRST_ID_FIELD_MAPPING[individual_info['idType']]
            field_input = []
            for field in field_list:
                if field in individual_info:
                    field_input.append((field, individual_info.get(field, '')))
            license_dict[license_en] = field_input

        if individual_info.get('secondIdType') in consts.CA_SECOND_ID_FIELD_MAPPING:
            second_license_en, second_field_list = consts.CA_SECOND_ID_FIELD_MAPPING[individual_info['secondIdType']]
            if second_license_en not in license_dict:
                second_field_input = []
                for second_field, write_field in second_field_list:
                    if second_field in individual_info:
                        second_field_input.append((write_field, individual_info.get(second_field, '')))
                license_dict[second_license_en] = second_field_input

        if individual_info['customerType'] == consts.CUSTOMER_TYPE[5]:
            sep_field_input = []
            for sep_field in consts.CA_SEP_FIELD:
                if sep_field in individual_info:
                    sep_field_input.append((sep_field, individual_info.get(sep_field, '')))
            license_dict[consts.BL_EN] = sep_field_input

        if len(license_dict) > 0:
            individual_info_dict.setdefault(individual_info['applicantType'], []).append(license_dict)

    compare_info['individualCusInfo'] = individual_info_dict

    if isinstance(last_obj.corporate_cus_info, str):
        corporate_info_dict = json.loads(last_obj.corporate_cus_info)
        corporate_info = {}
        bl_field_input = []
        for bl_field, write_field in consts.CA_CORPORATE_FIELD:
            bl_field_input.append((write_field, corporate_info_dict.get(bl_field, '')))
        corporate_info[consts.BL_EN] = bl_field_input

        compare_info['corporateCusInfo'] = corporate_info

    if last_obj.vehicle_status == consts.VEHICLE_STATUS[0] and isinstance(last_obj.usedcar_info, str):
        usedcar_info_dict = json.loads(last_obj.usedcar_info)
        usedcar_info = {}

        mvc_field_input = []
        for mvc_field in consts.CA_MVC_FIELD:
            mvc_field_input.append((mvc_field, usedcar_info_dict.get(mvc_field, '')))
        usedcar_info[consts.MVC_EN] = mvc_field_input

        dl_field_input = []
        for dl_field in consts.CA_DL_FIELD:
            dl_field_input.append((dl_field, usedcar_info_dict.get(dl_field, '')))
        usedcar_info[consts.DL_EN] = dl_field_input

        compare_info['usedCarInfo'] = usedcar_info

    return compare_info


def ca_compare_license(license_en, ocr_res_dict, field_list):
    ocr_field, compare_logic, special_expiry_date = consts.CA_COMPARE_FIELD[license_en]

    is_find = False
    result_field_list = []
    ocr_res_str = ocr_res_dict.get(ocr_field)
    if ocr_res_str is not None:
        ocr_res_list = json.loads(ocr_res_str)
        length = len(ocr_res_list)

        # 身份证、居住证 过期期限特殊处理
        if special_expiry_date:
            expiry_dates = set()
            expiry_dates_img_path = set()
            key = compare_logic.get('idExpiryDate')[0]
            for ocr_res in ocr_res_list:
                if key in ocr_res:
                    expiry_dates.add(ocr_res[key])
                    expiry_dates_img_path.add(ocr_res.get(consts.IMG_PATH_KEY_2, ''))
        else:
            expiry_dates = set()
            expiry_dates_img_path = set()

        for res_idx in range(length-1, -1, -1):
            if is_find:
                break

            for idx, (name, value) in enumerate(field_list):
                ocr_str = ocr_res_list[res_idx].get(compare_logic[name][0])
                if not isinstance(ocr_str, str):
                    result = consts.RESULT_N
                    ocr_str = empty_str
                else:
                    result = getattr(cp, compare_logic[name][1])(value, ocr_str, **compare_logic[name][2])

                if idx == 0 and result == consts.RESULT_N and length > 1:
                    break

                is_find = True

                # 过期期限特殊处理
                if special_expiry_date and name == 'idExpiryDate' and result == consts.RESULT_N:
                    for expiry_date in expiry_dates:
                        expiry_date_res = getattr(cp, compare_logic[name][1])(value, expiry_date, **compare_logic[name][2])
                        if expiry_date_res == consts.RESULT_Y:
                            ocr_str = expiry_date
                            result = expiry_date_res
                            break

                    if result == consts.RESULT_N:
                        next_img_path = expiry_dates_img_path.pop() if len(expiry_dates_img_path) > 0 else empty_str
                        img_path = ocr_res_list[res_idx].get(consts.IMG_PATH_KEY_2, next_img_path)
                    else:
                        img_path = empty_str
                else:
                    img_path = ocr_res_list[res_idx].get(consts.IMG_PATH_KEY, '') if result == consts.RESULT_N else empty_str
                error_type = empty_error_type if result == consts.RESULT_Y else ErrorType.OCR.value
                result_field_list.append((name, value, result, ocr_str, img_path, error_type))

    if not is_find:
        for name, value in field_list:
            result_field_list.append((name, value, consts.RESULT_N, empty_str, empty_str, ErrorType.NF.value))

    return result_field_list


def ca_compare_process(compare_info, ocr_res_dict):
    # individualCusInfo
    # corporateCusInfo
    # usedCarInfo
    compare_result = []
    total_fields = 0
    failed_count = 0

    for info_key, info_value in compare_info.items():
        if info_key == 'individualCusInfo':
            for idx, license_list in info_value.items():
                for license_dict in license_list:
                    for license_en, field_list in license_dict.items():
                        result_field_list = ca_compare_license(license_en, ocr_res_dict, field_list)
                        for name, value, result, ocr_str, img_path, error_type in result_field_list:
                            total_fields += 1
                            if result == consts.RESULT_N:
                                failed_count += 1
                            compare_result.append(
                                {
                                    consts.HEAD_LIST[0]: info_key,
                                    consts.HEAD_LIST[1]: idx,
                                    consts.HEAD_LIST[2]: license_en,
                                    consts.HEAD_LIST[3]: name,
                                    consts.HEAD_LIST[4]: value,
                                    consts.HEAD_LIST[5]: ocr_str,
                                    consts.HEAD_LIST[6]: result,
                                    consts.HEAD_LIST[7]: empty_str,
                                    consts.HEAD_LIST[8]: img_path,
                                    consts.HEAD_LIST[9]: error_type,
                                }
                            )
        else:
            for license_en, field_list in info_value.items():
                result_field_list = ca_compare_license(license_en, ocr_res_dict, field_list)
                for name, value, result, ocr_str, img_path, error_type in result_field_list:
                    total_fields += 1
                    if result == consts.RESULT_N:
                        failed_count += 1
                    compare_result.append(
                        {
                            consts.HEAD_LIST[0]: info_key,
                            consts.HEAD_LIST[1]: "0",
                            consts.HEAD_LIST[2]: license_en,
                            consts.HEAD_LIST[3]: name,
                            consts.HEAD_LIST[4]: value,
                            consts.HEAD_LIST[5]: ocr_str,
                            consts.HEAD_LIST[6]: result,
                            consts.HEAD_LIST[7]: empty_str,
                            consts.HEAD_LIST[8]: img_path,
                            consts.HEAD_LIST[9]: error_type,
                        }
                    )
    return compare_result, total_fields, failed_count


def ca_compare(application_id, application_entity, ocr_res_id, last_obj, ocr_res_dict):
    start_time = datetime.now()

    try:
        # 比对逻辑
        compare_info = get_ca_compare_info(last_obj)
        compare_result, total_fields, failed_count = ca_compare_process(compare_info, ocr_res_dict)
        compare_log.info('{0} [CA] [compare success] [entity={1}] [id={2}] [ocr_res_id={3}] [result={4}]'.format(
            log_base, application_entity, application_id, ocr_res_id, compare_result))
    except Exception as e:
        compare_log.error('{0} [CA] [compare error] [entity={1}] [id={2}] [ocr_res_id={3}] '
                          '[error={4}]'.format(log_base, application_entity, application_id, ocr_res_id,
                                               traceback.format_exc()))
    else:
        # 将比对结果写入数据库
        try:
            result_table = HILCACompareResult if application_entity == consts.HIL_PREFIX else AFCCACompareResult
            res_obj = result_table.objects.filter(application_id=application_id).first()
            if res_obj is None:
                res_obj = result_table()
                res_obj.application_id = application_id
            res_obj.compare_count = total_fields
            res_obj.failed_count = failed_count
            res_obj.is_finish = failed_count == 0
            res_obj.version = '{0}{1}{2}'.format(consts.INFO_SOURCE[0], consts.SPLIT_STR, last_obj.application_version)
            # res_obj.reason1_count = reason1_count
            res_obj.result = json.dumps(compare_result)
            res_obj.update_time = start_time
            res_obj.save()
            compare_log.info('{0} [CA] [result save success] [entity={1}] [id={2}] [ocr_res_id={3}]'.format(
                log_base, application_entity, application_id, ocr_res_id))
        except Exception as e:
            compare_log.error('{0} [CA] [result save error] [entity={1}] [id={2}] [ocr_res_id={3}] '
                              '[error={4}]'.format(log_base, application_entity, application_id, ocr_res_id,
                                                   traceback.format_exc()))

    compare_failed = False
    application_link = '{0}/showList/showList?entity={1}&scheme={2}&case_id={3}'.format(
        conf.BASE_URL, application_entity, consts.COMPARE_DOC_SCHEME_LIST[0], application_id)

    # 比对信息
    try:
        comparison_res = OrderedDict({
            'OCR_Input': {
                'uniqSeq': last_obj.uniq_seq,
                'applicationId': application_id,
                'applicationEntity': application_entity,
                'applicationVersion': last_obj.application_version,
                'vehicleStatus': last_obj.vehicle_status,
                'wholeResult': 'N',
                'wholeResultMessage': empty_str,
                'applicationLink': application_link.replace('&', '&'),
            }
        })

        res_set = set()

        # is_sep = True if last_obj.customer_type == consts.CUSTOMER_TYPE[5] else False

        individual_cus_info_list = json.loads(last_obj.individual_cus_info)
        order_individual_cus_info_list = []
        for individual_cus_info in individual_cus_info_list:
            order_individual_cus_info = get_order_dict(individual_cus_info, consts.IN_ORDER)

            cus_type = order_individual_cus_info.get('customerType')

            # 获取sep下营业执照法人代表
            if cus_type == consts.CUSTOMER_TYPE[5]:
                sep_name = order_individual_cus_info.get('customerChineseName')
                if isinstance(sep_name, str):
                    sep_name = sep_name.strip()
                    if sep_name == empty_str:
                        sep_name = None
            else:
                sep_name = None

            # 个人信息证件
            id_type = order_individual_cus_info.get('idType')
            if id_type not in consts.ID_TYPE_COMPARE:
                do_not_compare(order_individual_cus_info, consts.ITPRC)
            else:
                ocr_field, compare_list, has_expiry_date = consts.ID_TYPE_COMPARE.get(id_type)
                field_compare(order_individual_cus_info, ocr_res_dict, ocr_field, compare_list, res_set,
                              has_expiry_date=has_expiry_date)

            # 第二证件
            second_id_type = order_individual_cus_info.get('secondIdType')
            if second_id_type is not None:
                if second_id_type not in consts.SECOND_ID_TYPE_COMPARE:
                    do_not_compare(order_individual_cus_info, consts.SECOND_ITPRC)
                else:
                    second_ocr_field, second_compare_list = consts.SECOND_ID_TYPE_COMPARE.get(second_id_type)
                    field_compare(order_individual_cus_info, ocr_res_dict, second_ocr_field,
                                  second_compare_list, res_set)

                # 姓名比对
                second_id_res = order_individual_cus_info.pop(consts.SECOND_ID_RES, consts.RESULT_NA)
                if second_id_res == consts.RESULT_Y:
                    second_id_num = order_individual_cus_info.get(consts.SECOND_ID_FIELD)
                    name = order_individual_cus_info.get(consts.NAME_FIELD)
                    if isinstance(second_id_num, str) and isinstance(name, str):
                        second_ocr_field, second_compare_list, _ = consts.ID_TYPE_COMPARE.get(second_id_type)
                        name_right = name_check(ocr_res_dict, second_ocr_field, second_compare_list, second_id_num, name)
                        if not name_right:
                            res_set.add(consts.RESULT_N)
                            second_id_res = consts.RESULT_N
                            order_individual_cus_info[consts.SECOND_ID_FIELD] = '{0}-{1}'.format(second_id_num, name)

                # 重新排列
                new_dict = OrderedDict({})
                for key, value in order_individual_cus_info.items():
                    new_dict[key] = value
                    if key == consts.ID_RES:
                        new_dict[consts.SECOND_ID_RES] = second_id_res
                order_individual_cus_info = new_dict

            # sep营业执照
            if cus_type == consts.CUSTOMER_TYPE[5]:
                field_compare(order_individual_cus_info, ocr_res_dict, consts.BL_OCR_FIELD, consts.TCSEP, res_set,
                              sep_name=sep_name, capital_ignore=True)

            order_individual_cus_info_list.append(order_individual_cus_info)

        comparison_res['OCR_Input']['individualCusInfo'] = order_individual_cus_info_list

        if last_obj.vehicle_status == consts.VEHICLE_STATUS[0] and last_obj.usedcar_info is not None:
            usedcar_info = json.loads(last_obj.usedcar_info)
            order_usedcar_info = get_order_dict(usedcar_info, consts.UC_ORDER)

            mvc_find, mvc_vinos = usedcar_info_compare(order_usedcar_info, ocr_res_dict, consts.MVC_OCR_FIELD,
                                                       consts.PCUSD_MVC, res_set)

            # if order_usedcar_info[consts.PCUSD_MVC[0][0] + 'Result'] == consts.RESULT_Y:
            dl_find, dl_vinos = usedcar_info_compare(order_usedcar_info, ocr_res_dict, consts.DL_OCR_FIELD,
                                                     consts.PCUSD_DL, res_set)

            if mvc_find is True and dl_find is False:
                vino = dl_vinos[-1] if len(dl_vinos) > 0 else empty_str
                order_usedcar_info[consts.PCUSD_MVC[0][0]] = '{0}-{1} {2} {3}-{4}'.format(
                    consts.PREFIX_MVC, consts.RESULT_Y, consts.SPLIT, consts.PREFIX_DL, vino)
                order_usedcar_info[consts.PCUSD_MVC[0][4]] = consts.RESULT_N
            elif mvc_find is False and dl_find is True:
                vino = mvc_vinos[-1] if len(mvc_vinos) > 0 else empty_str
                order_usedcar_info[consts.PCUSD_MVC[0][0]] = '{0}-{1} {2} {3}-{4}'.format(
                    consts.PREFIX_MVC, vino, consts.SPLIT, consts.PREFIX_DL, consts.RESULT_Y)
                order_usedcar_info[consts.PCUSD_MVC[0][4]] = consts.RESULT_N
            elif mvc_find is False and dl_find is False:
                if len(mvc_vinos) == 0 and len(dl_vinos) == 0:
                    order_usedcar_info[consts.PCUSD_MVC[0][0]] = None
                    order_usedcar_info[consts.PCUSD_MVC[0][4]] = consts.RESULT_NA
                else:
                    mvc_vino = mvc_vinos[-1] if len(mvc_vinos) > 0 else empty_str
                    dl_vino = dl_vinos[-1] if len(dl_vinos) > 0 else empty_str
                    vino = '{0}-{1} {2} {3}-{4}'.format(
                        consts.PREFIX_MVC, mvc_vino, consts.SPLIT, consts.PREFIX_DL, dl_vino)
                    order_usedcar_info[consts.PCUSD_MVC[0][0]] = vino
                    order_usedcar_info[consts.PCUSD_MVC[0][4]] = consts.RESULT_N

            comparison_res['OCR_Input']['usedCarInfo'] = order_usedcar_info

        if last_obj.corporate_cus_info is not None:
            corporate_cus_info = json.loads(last_obj.corporate_cus_info)
            order_corporate_cus_info = get_order_dict(corporate_cus_info, consts.CO_ORDER)
            field_compare(order_corporate_cus_info, ocr_res_dict, consts.BL_OCR_FIELD, consts.TCCOR, res_set)

            comparison_res['OCR_Input']['corporateCusInfo'] = order_corporate_cus_info

        comparison_res['OCR_Input'][
            'wholeResult'] = consts.RESULT_N if consts.RESULT_N in res_set or consts.RESULT_NA in res_set else consts.RESULT_Y

    except Exception as e:
        compare_failed = True
        compare_log.error('{0} [CA] [compare error] [entity={1}] [id={2}] [ocr_res_id={3}] [error={4}]'.format(
            log_base, application_entity, application_id, ocr_res_id, traceback.format_exc()))
    else:
        compare_log.info('{0} [CA] [compare success] [entity={1}] [id={2}] [ocr_res_id={3}] [compare_res={4}]'.format(
            log_base, application_entity, application_id, ocr_res_id, comparison_res))

        is_gcap_send = Configs.objects.filter(id=1).first()
        if is_gcap_send is not None and is_gcap_send.value == 'N':
            compare_log.info('{0} [CA] [gcap closed] [entity={1}] [id={2}] [ocr_res_id={3}]'.format(
                log_base, application_entity, application_id, ocr_res_id))
            return

        # 时间延迟
        send_time = last_obj.create_time + timedelta(seconds=15)
        while datetime.now() < send_time:
            compare_log.info('{0} [CA] [time wait 5s] [entity={1}] [id={2}] [ocr_res_id={3}]'.format(
                log_base, application_entity, application_id, ocr_res_id))
            time.sleep(5)

        # 将比对结果发送GCAP
        try:
            data = gcap.dict_to_xml(comparison_res)
        except Exception as e:
            compare_log.error('{0} [CA] [dict to xml failed] [entity={1}] [id={2}] [ocr_res_id={3}] [error={4}]'.format(
                log_base, application_entity, application_id, ocr_res_id, traceback.format_exc()))
        else:
            try:
                for times in range(consts.RETRY_TIMES):
                    try:
                        res_text = gcap.send(data)
                    except Exception as e:
                        gcap_exc = str(e)
                    else:
                        break
                else:
                    raise GCAPException(gcap_exc)
            except Exception as e:
                compare_log.error('{0} [CA] [gcap failed] [entity={1}] [id={2}] [ocr_res_id={3}] [error={4}]'.format(
                    log_base, application_entity, application_id, ocr_res_id, traceback.format_exc()))
            else:
                compare_log.info('{0} [CA] [gcap success] [entity={1}] [id={2}] [ocr_res_id={3}] [response={4}]'.format(
                    log_base, application_entity, application_id, ocr_res_id, res_text))
                compare_log.info('{0} [CA] [task success] [entity={1}] [id={2}] [ocr_res_id={3}]'.format(
                    log_base, application_entity, application_id, ocr_res_id))

        # report
        try:
            end_time = datetime.now()
            if compare_failed:
                successful_at_this_level = False
                failure_reason = 'Compare process error'
                total_fields = 0
            else:
                successful_at_this_level = True if comparison_res['OCR_Input'][
                                                       'wholeResult'] == consts.RESULT_Y else False
                field_failed = {
                    'individualCusInfo': [],
                    'corporateCusInfo': [],
                    'usedCarInfo': []
                }

                individual_list = comparison_res.get('OCR_Input', {}).get('individualCusInfo', [])
                total_fields = 0

                for individual in individual_list:

                    field_list = []
                    if individual.get('idType') in consts.ID_TYPE_COMPARE:
                        total_fields += 4
                        if not successful_at_this_level:
                            for field_name, _, _, _, result_field in consts.ITPRC:
                                if individual.get(result_field) != consts.RESULT_Y:
                                    field_list.append(field_name)

                    if individual.get('secondIdType') in consts.SECOND_ID_TYPE_COMPARE:
                        total_fields += 1
                        if not successful_at_this_level:
                            if individual.get(consts.SECOND_ID_RES) != consts.RESULT_Y:
                                field_list.append(consts.SECOND_ID_FIELD)

                    if individual.get('customerType') == consts.CUSTOMER_TYPE[5]:
                        total_fields += 3
                        if not successful_at_this_level:
                            for field_name, _, _, _, result_field in consts.TCSEP:
                                if individual.get(result_field) != consts.RESULT_Y:
                                    field_list.append(field_name)

                    if len(field_list) > 0:
                        field_failed['individualCusInfo'].append(';'.join(field_list))

                corporate_res = comparison_res.get('OCR_Input', {}).get('corporateCusInfo', {})
                if corporate_res is not None:
                    total_fields += 8
                    if not successful_at_this_level:
                        corporate_field_list = []
                        for field_name, _, _, _, result_field in consts.TCCOR:
                            if corporate_res.get(result_field) != consts.RESULT_Y:
                                corporate_field_list.append(field_name)
                        if len(corporate_field_list) > 0:
                            field_failed['corporateCusInfo'].append(';'.join(corporate_field_list))

                used_car_res = comparison_res.get('OCR_Input', {}).get('usedCarInfo', {})
                if used_car_res is not None:
                    total_fields += 3
                    if not successful_at_this_level:
                        used_car_field_list = []
                        for field_name, _, _, _, result_field in consts.PCUSD_MVC:
                            if used_car_res.get(result_field) != consts.RESULT_Y:
                                used_car_field_list.append(field_name)
                        if len(used_car_field_list) > 0:
                            field_failed['usedCarInfo'].append(';'.join(used_car_field_list))

                if not successful_at_this_level:
                    reason_list = []
                    for key, value in field_failed.items():
                        if len(value) > 0:
                            value_str = json.dumps(value)
                            reason_list.append('{0}: {1}'.format(key, value_str))
                    failure_reason = '、'.join(reason_list)

                else:
                    failure_reason = empty_str

            request_trigger = RequestTrigger.SUBMITING.value if ocr_res_id is None else RequestTrigger.UPLOADING.value
            report_class = HILCompareReport if application_entity == consts.HIL_PREFIX else AFCCompareReport
            report_class.objects.create(
                case_number=application_id,
                request_team=RequestTeam.ACCEPTANCE.value,
                request_trigger=request_trigger,
                transaction_start=start_time,
                transaction_end=end_time,
                successful_at_this_level=successful_at_this_level,
                failure_reason=failure_reason,
                process_name=ProcessName.CACOMPARE.value,
                total_fields=total_fields,
                workflow_name=last_obj.customer_type,
            )
            compare_log.info(
                '{0} [CA] [report save success] [entity={1}] [id={2}] [ocr_res_id={3}]'.format(
                    log_base, application_entity, application_id, ocr_res_id))
        except Exception as e:
            compare_log.error('{0} [CA] [report save error] [entity={1}] [id={2}] [ocr_res_id={3}] '
                              '[error={4}]'.format(log_base, application_entity, application_id, ocr_res_id,
                                                   traceback.format_exc()))


def get_se_compare_info(last_obj, application_entity, detect_list):
    compare_info = {}
    individual_info_dict = {}
    main_role_info = {}
    for individual_info in json.loads(last_obj.individual_cus_info):
        if isinstance(individual_info.get('secondIdNum'), str):
            id_num = '{0}、{1}'.format(individual_info['idNum'], individual_info['secondIdNum'])
        else:
            id_num = individual_info['idNum']
        main_role_info.setdefault(individual_info['applicantType'], []).append(
            (individual_info['customerName'], id_num, individual_info['idNum']))

        license_dict = {}

        if individual_info['idType'] in consts.SE_FIRST_ID_FIELD_MAPPING:
            license_en, field_list = consts.SE_FIRST_ID_FIELD_MAPPING[individual_info['idType']]
            field_input = []
            for field in field_list:
                if field in individual_info:
                    field_input.append((field, individual_info[field]))
            license_dict[license_en] = field_input

        if individual_info.get('secondIdType') in consts.SE_SECOND_ID_FIELD_MAPPING:
            second_license_en, second_field_list = consts.SE_SECOND_ID_FIELD_MAPPING[individual_info['secondIdType']]
            if second_license_en not in license_dict:
                second_field_input = []
                for second_field, write_field in second_field_list:
                    if second_field in individual_info:
                        second_field_input.append((write_field, individual_info[second_field]))
                license_dict[second_license_en] = second_field_input

        if individual_info['customerType'] == consts.CUSTOMER_TYPE[5]:
            sep_field_input = []
            for sep_field, sep_write_field in consts.SE_SEP_FIELD:
                if sep_field in individual_info:
                    sep_field_input.append((sep_write_field, individual_info[sep_field]))
            license_dict[consts.BL_EN] = sep_field_input

        if len(license_dict) > 0:
            individual_info_dict.setdefault(individual_info['applicantType'], []).append(license_dict)

    compare_info['individualCusInfo'] = individual_info_dict

    dda_name_list = []
    dda_num_list = []
    for applicant_type in consts.APPLICANT_TYPE:
        if applicant_type in main_role_info:
            main_name, main_num, _ = main_role_info[applicant_type][0]
            hmh_name, _, hmh_id = main_role_info[applicant_type][0]
            break
    for applicant_type in consts.APPLICANT_TYPE[:2]:
        if applicant_type in main_role_info:
            for dda_name_part, _, dda_num_part in main_role_info[applicant_type]:
                dda_name_list.append(dda_name_part)
                dda_num_list.append(dda_num_part)
    dda_name = '、'.join(dda_name_list)
    dda_num = '、'.join(dda_num_list)
    del main_role_info

    company_info = None
    if isinstance(last_obj.corporate_cus_info, str):
        corporate_info_dict = json.loads(last_obj.corporate_cus_info)
        corporate_info = {}
        company_info = (corporate_info_dict['companyName'], corporate_info_dict['businessLicenseNo'])
        if corporate_info_dict['firstIdType'] == consts.ID_TYPE[6]:
            bl_field_input = []
            for bl_field in consts.SE_CORPORATE_ALL_FIELD:
                bl_field_input.append((bl_field, corporate_info_dict[bl_field]))
            corporate_info[consts.BL_EN] = bl_field_input
        else:
            if corporate_info_dict['firstIdType'] in consts.SE_FIRST_ID_FIELD_MAPPING:
                license_en, _ = consts.SE_FIRST_ID_FIELD_MAPPING[corporate_info_dict['firstIdType']]
                first_id_field_input = []
                for first_id_field, first_id_write_field in consts.SE_CORPORATE_ID_FIELD:
                    first_id_field_input.append((first_id_write_field, corporate_info_dict[first_id_field]))
                corporate_info[license_en] = first_id_field_input
            bl_field_input = []
            for bl_field in consts.SE_CORPORATE_FIELD:
                bl_field_input.append((bl_field, corporate_info_dict[bl_field]))
            corporate_info[consts.BL_EN] = bl_field_input

        compare_info['corporateCusInfo'] = corporate_info

    vehicle_info_dict = json.loads(last_obj.vehicle_info)  # TODO 车辆登记证
    vehicle_info = {}
    vehicle_field_input = []
    if vehicle_info_dict['vehicleStatus'] == 'New':
        for vehicle_field in consts.SE_NEW_FIELD:
            vehicle_field_input.append((vehicle_field, vehicle_info_dict[vehicle_field]))
        if isinstance(company_info, tuple):
            vehicle_field_input.append((consts.SE_NEW_ADD_FIELD[0], company_info[0]))
            vehicle_field_input.append((consts.SE_NEW_ADD_FIELD[1], company_info[1]))
        else:
            vehicle_field_input.append((consts.SE_NEW_ADD_FIELD[0], main_name))
            vehicle_field_input.append((consts.SE_NEW_ADD_FIELD[1], main_num))
        vehicle_field_input.append((consts.SE_NEW_ADD_FIELD[2], last_obj.first_submmison_date))
        vehicle_field_input.append((consts.SE_NEW_ADD_FIELD[3], consts.SE_STAMP_VALUE))
        vehicle_info[consts.MVI_EN] = vehicle_field_input
    else:
        gb_field_input = []
        gb34_field_input = []
        jypz_field_input = []
        for vehicle_field in consts.SE_USED_FIELD:
            vehicle_field_input.append((vehicle_field, vehicle_info_dict[vehicle_field]))
        for gb_field in consts.SE_GB_NEW_FIELD:
            gb_field_input.append((gb_field, vehicle_info_dict[gb_field]))
        if isinstance(company_info, tuple):
            vehicle_field_input.append((consts.SE_NEW_ADD_FIELD[0], company_info[0]))
            vehicle_field_input.append((consts.SE_NEW_ADD_FIELD[1], company_info[1]))
            jypz_field_input.append((consts.SE_NEW_ADD_FIELD[0], company_info[0]))
            jypz_field_input.append((consts.SE_NEW_ADD_FIELD[1], company_info[1]))
            gb34_field_input.append((consts.SE_GB_USED_FIELD[0], company_info[0]))
            gb34_field_input.append((consts.SE_GB_USED_FIELD[1], company_info[1]))
        else:
            vehicle_field_input.append((consts.SE_NEW_ADD_FIELD[0], main_name))
            vehicle_field_input.append((consts.SE_NEW_ADD_FIELD[1], main_num))
            jypz_field_input.append((consts.SE_NEW_ADD_FIELD[0], main_name))
            jypz_field_input.append((consts.SE_NEW_ADD_FIELD[1], main_num))
            gb34_field_input.append((consts.SE_GB_USED_FIELD[0], main_name))
            gb34_field_input.append((consts.SE_GB_USED_FIELD[1], main_num))
        gb34_field_input.append((consts.SE_GB_USED_FIELD[2], last_obj.first_submmison_date))
        vehicle_field_input.append((consts.SE_NEW_ADD_FIELD[2], last_obj.first_submmison_date))
        vehicle_field_input.append((consts.SE_NEW_ADD_FIELD[3], consts.SE_STAMP_VALUE))
        for jypz_field in consts.SE_USED_FIELD:
            jypz_field_input.append((jypz_field, vehicle_info_dict[jypz_field]))
        jypz_field_input.append((consts.SE_GB_USED_FIELD[-1], last_obj.first_submmison_date))
        vehicle_info[consts.MVC_EN] = gb_field_input
        vehicle_info[consts.MVC34_EN] = gb34_field_input
        if not detect_list[0]:
            vehicle_info[consts.UCI_EN] = vehicle_field_input
        if not detect_list[1]:
            vehicle_info[consts.JYPZ_EN] = jypz_field_input
        if detect_list[0] and detect_list[1]:
            vehicle_info[consts.UCI_EN] = vehicle_field_input
    compare_info['vehicleInfo'] = vehicle_info

    bank_info_dict = json.loads(last_obj.bank_info)
    bank_info = {}
    bank_field_input = []
    for bank_field in consts.SE_BANK_FIELD:
        bank_field_input.append((bank_field, bank_info_dict[bank_field]))
    if '工商' in bank_info_dict.get(consts.SE_BANK_FIELD[-1], ''):
        is_gsyh = True
    else:
        is_gsyh = False
    bank_info[consts.BC_EN] = bank_field_input

    dda_field_input = []
    for dda_field in consts.SE_DDA_FIELD:
        if dda_field.startswith('applicationId'):
            dda_field_input.append((dda_field, last_obj.application_id))
        elif dda_field == 'companyName':
            company_name = consts.HIL_COMPANY_NAME if application_entity in consts.HIL_SET else consts.AFC_COMPANY_NAME
            dda_field_input.append((dda_field, company_name))
        elif dda_field == 'customerName':
            dda_field_input.append((dda_field, dda_name))
        elif dda_field == 'idNum':
            dda_field_input.append((dda_field, dda_num))
        else:
            dda_field_input.append((dda_field, bank_info_dict[dda_field]))
    bank_info[consts.DDA_EN] = dda_field_input
    compare_info['bankInfo'] = bank_info

    if not detect_list[-1]:
        other_info = {}
        hmh_field_input = []
        hmh_field_input.append((consts.SE_HMH_FIELD[0], hmh_name))
        hmh_field_input.append((consts.SE_HMH_FIELD[1], hmh_id))
        hmh_field_input.append((consts.SE_HMH_FIELD[2], last_obj.application_id))
        other_info[consts.HMH_EN] = hmh_field_input
        compare_info['other'] = other_info

    return compare_info, is_gsyh


def rebuild_compare_info(last_obj):
    # {
    #     "content": {
    #         "financeCompany": "宝马汽车金融有限公司",
    #         "contractNo": "CH-B100000123",
    #         "status": "HIL",
    #         "branch": "宝马汽车金融有限公司",
    #         "fpCampaign": "Q1_2021_BMW_BASIC",
    #         "applicationVersion": 1,
    #         "submissionDate": {},
    #         "mortgageType": "Mortgage Contract",
    #         "dealerRegion": "West",
    #         "insuranceRealNameCity": false,
    #         "totalFinanceAmount": 1234.56,
    #         "terms": 24,
    #         "dealerName": "乐山长宝汽车销售服务有限公司",
    #         "tier": "2",
    #         "province": "四川省",
    #         "fapiaoIssuerDealer": "乐山长宝汽车销售服务有限公司",
    #         "customerName": "蔡红",
    #         "customerIdNo": "511102196706080000",
    #         "vehicleStatus": "Used",
    #         "applicationSource": "eApp",
    #         "contractSource": "Online Sign",
    #         "applicationRating": 100,
    #         "applicantInformation": [
    #             {
    #                 "applicantType": "Borrower",
    #                 "customersubType": "TCCOR",
    #                 "selfEmployedSubType": "CSIBM",
    #                 "name": "李四",
    #                 "legalRepName": "张三",
    #                 "dateOfBirth": {},
    #                 "nationality": "中国",
    #                 "establishmentDate": {},
    #                 "IDInformation": [
    #                     {
    #                         "idType": "ITARI",
    #                         "idNum": "111111199404251100",
    #                         "idExpiryDate": {}
    #                     }
    #                 ]
    #             }
    #         ],
    #         "autoApprovedDetails": {
    #             "aaType": "CAA1"
    #         },
    #         "financialInformation": {
    #             "vehiclePrice": 1234.56,
    #             "grossPrice": 1234.56,
    #             "associatedServicePrice": 1234.56,
    #             "vehiclePrincipal": 1234.56,
    #             "associatedServicePrincipal": 1234.56,
    #             "originationPrincipal": 1234.56,
    #             "totalDownPayment": 1234.56,
    #             "vehicleDownPaymentRatio": 1234.56,
    #             "optionAmount": 1234.56,
    #             "sumOfMSRPAndOption": 1234.56
    #         },
    #         "paymentSchedule": [
    #             {
    #                 "no": 3,
    #                 "grossRentalAmount": 1234.56
    #             }
    #         ],
    #         "associatedServices": [
    #             {
    #                 "associatedServices": "机动车保险",
    #                 "price": 1234.56,
    #                 "financed": 1234.56,
    #                 "total": 1234.56
    #             }
    #         ],
    #         "vehicleInformation": {
    #             "vinNo": "LBV23482934DJFKD"
    #         },
    #         "bankAccountDetails": {
    #             "bankName": "中国银行",
    #             "accountHolderName": "张三",
    #             "accountNo": "12312123123123124"
    #         },
    #         "insuranceDetails": {
    #             "insuranceType": "ComprehensiveInsurance",
    #             "insuranceAmount": "60000000",
    #             "startDate": {},
    #             "endDate": {}
    #         },
    #         "corporateFinancialInformation": {
    #             "hashCode": "238231",
    #             "borrowerName": "张三",
    #             "fiscalYear": 2019,
    #             "totaAssets": 1234.56,
    #             "totalLiabilitiesAndOwnersEquity": 1234.56,
    #             "cashAndCashEquivalentAtEndOfPeriod": 1234.56,
    #             "netProfit": 1234.56
    #         },
    #         "settlemnetVerification": {
    #             "applicationNo": "CH-B100000123"
    #         }
    #     }
    # }

    return FakePOS(application_id,
                   first_submmison_date,
                   application_version,
                   customer_type,
                   individual_cus_info,
                   corporate_cus_info,
                   vehicle_info,
                   bank_info)


def se_compare_license(license_en, ocr_res_dict, field_list):
    ocr_field, compare_logic, special_expiry_date = consts.SE_COMPARE_FIELD[license_en]

    is_find = False
    no_ocr_result = False
    result_field_list = []
    ocr_res_str = ocr_res_dict.get(ocr_field)
    if ocr_res_str is not None:
        ocr_res_list = json.loads(ocr_res_str)
        length = len(ocr_res_list)

        # 身份证、居住证 过期期限特殊处理
        if special_expiry_date:
            expiry_dates = set()
            key = compare_logic.get('idExpiryDate')[0]
            for ocr_res in ocr_res_list:
                if key in ocr_res:
                    expiry_dates.add(ocr_res[key])
        else:
            expiry_dates = set()

        for res_idx in range(length-1, -1, -1):
            if is_find:
                break

            for idx, (name, value) in enumerate(field_list):
                ocr_str = ocr_res_list[res_idx].get(compare_logic[name][0])
                if not isinstance(ocr_str, str):
                    result = consts.RESULT_N
                    ocr_str = empty_str
                else:
                    result = getattr(cp, compare_logic[name][1])(value, ocr_str, **compare_logic[name][2])

                if idx == 0 and result == consts.RESULT_N and length > 1:
                    break

                is_find = True

                # 过期期限特殊处理
                if special_expiry_date and name == 'idExpiryDate' and result == consts.RESULT_N:
                    for expiry_date in expiry_dates:
                        expiry_date_res = getattr(cp, compare_logic[name][1])(value, expiry_date, **compare_logic[name][2])
                        if expiry_date_res == consts.RESULT_Y:
                            ocr_str = expiry_date
                            result = expiry_date_res
                            break

                img_path = ocr_res_list[res_idx].get(consts.IMG_PATH_KEY, '') if result == consts.RESULT_N else empty_str
                error_type = empty_error_type if result == consts.RESULT_Y else ErrorType.OCR.value
                result_field_list.append((name, value, result, ocr_str, img_path, error_type))
    else:
        no_ocr_result = True

    if not is_find:
        for name, value in field_list:
            result_field_list.append((name, value, consts.RESULT_N, empty_str, empty_str, ErrorType.NF.value))

    return result_field_list, no_ocr_result


def se_mvc34_compare(license_en, ocr_res_dict, field_list):
    ocr_field, compare_logic, _ = consts.SE_COMPARE_FIELD[license_en]
    ocr_res_str = ocr_res_dict.get(ocr_field)

    is_find = False
    result_field_list = []

    if ocr_res_str is not None:
        ocr_res_list = json.loads(ocr_res_str)
        length = len(ocr_res_list)

        page34_date_dict = dict()
        first_res = None
        for res_idx in range(length-1, -1, -1):
            if consts.TRANSFER_DATE in ocr_res_list[res_idx]:
                img_path = ocr_res_list[res_idx].get(consts.IMG_PATH_KEY, '')
                for idx, transfer_date in enumerate(ocr_res_list[res_idx].get(consts.TRANSFER_DATE, [])):
                    try:
                        transfer_name = ocr_res_list[res_idx].get(consts.TRANSFER_NAME, [])[idx]
                    except Exception as e:
                        transfer_name = empty_str
                    try:
                        transfer_num = ocr_res_list[res_idx].get(consts.TRANSFER_NUM, [])[idx]
                    except Exception as e:
                        transfer_num = empty_str
                    core_info = {
                        consts.TRANSFER_NAME: transfer_name,
                        consts.TRANSFER_NUM: transfer_num,
                        consts.TRANSFER_DATE: transfer_date,
                        consts.IMG_PATH_KEY: img_path,
                    }
                    page34_date_dict.setdefault(transfer_date, []).append(core_info)
                    if first_res is None:
                        first_res = core_info

        max_date = None
        for date_tmp in page34_date_dict.keys():
            try:
                max_date_part = time.strptime(date_tmp, "%Y-%m-%d")
            except Exception as e:
                pass
            else:
                if max_date is None or max_date_part > max_date:
                    max_date = max_date_part

        if max_date is not None or first_res is not None:
            is_find = True
            ocr_res = first_res if max_date is None else page34_date_dict[time.strftime('%Y-%m-%d', max_date)][0]
            for name, value in field_list:
                ocr_str = ocr_res.get(compare_logic[name][0])
                if not isinstance(ocr_str, str):
                    result = consts.RESULT_N
                    ocr_str = empty_str
                else:
                    result = getattr(cp, compare_logic[name][1])(value, ocr_str, **compare_logic[name][2])
                img_path = ocr_res.get(consts.IMG_PATH_KEY, '') if result == consts.RESULT_N else empty_str
                error_type = empty_error_type if result == consts.RESULT_Y else ErrorType.OCR.value
                result_field_list.append((name, value, result, ocr_str, img_path, error_type))

    if not is_find:
        for name, value in field_list:
            result_field_list.append((name, value, consts.RESULT_N, empty_str, empty_str, ErrorType.NF.value))

    return result_field_list


def se_compare_process(compare_info, ocr_res_dict, is_gsyh):
    # individualCusInfo
    # corporateCusInfo
    # vehicleInfo
    # bankInfo
    compare_result = []
    total_fields = 0
    failed_count = 0
    successful_at_this_level = True
    failure_reason = {}

    for info_key, info_value in compare_info.items():
        if info_key == 'individualCusInfo':
            for idx, license_list in info_value.items():
                for license_dict in license_list:
                    for license_en, field_list in license_dict.items():
                        failure_field = []
                        result_field_list, no_ocr_result = se_compare_license(license_en, ocr_res_dict, field_list)
                        for name, value, result, ocr_str, img_path, error_type in result_field_list:
                            if license_en not in consts.SKIP_CARD or not no_ocr_result:
                                total_fields += 1
                                if result == consts.RESULT_N:
                                    failed_count += 1
                                    successful_at_this_level = False
                                    failure_field.append(name)
                            compare_result.append(
                                {
                                    consts.HEAD_LIST[0]: info_key,
                                    consts.HEAD_LIST[1]: idx,
                                    consts.HEAD_LIST[2]: license_en,
                                    consts.HEAD_LIST[3]: name,
                                    consts.HEAD_LIST[4]: value,
                                    consts.HEAD_LIST[5]: ocr_str,
                                    consts.HEAD_LIST[6]: result,
                                    consts.HEAD_LIST[7]: empty_str,
                                    consts.HEAD_LIST[8]: img_path,
                                    consts.HEAD_LIST[9]: error_type,
                                }
                            )
                        if len(failure_field) > 0:
                            failure_reason.setdefault(license_en, []).append('/'.join(failure_field))
        else:
            for license_en, field_list in info_value.items():
                failure_field = []
                if license_en == consts.MVC34_EN:
                    result_field_list = se_mvc34_compare(license_en, ocr_res_dict, field_list)
                    no_ocr_result = False
                else:
                    result_field_list, no_ocr_result = se_compare_license(license_en, ocr_res_dict, field_list)
                for name, value, result, ocr_str, img_path, error_type in result_field_list:
                    if license_en != consts.DDA_EN or not no_ocr_result or is_gsyh:
                        total_fields += 1
                        if result == consts.RESULT_N:
                            failed_count += 1
                            successful_at_this_level = False
                            failure_field.append(name)
                    compare_result.append(
                        {
                            consts.HEAD_LIST[0]: info_key,
                            consts.HEAD_LIST[1]: "0",
                            consts.HEAD_LIST[2]: license_en,
                            consts.HEAD_LIST[3]: name,
                            consts.HEAD_LIST[4]: value,
                            consts.HEAD_LIST[5]: ocr_str,
                            consts.HEAD_LIST[6]: result,
                            consts.HEAD_LIST[7]: empty_str,
                            consts.HEAD_LIST[8]: img_path,
                            consts.HEAD_LIST[9]: error_type,
                        }
                    )
                if len(failure_field) > 0:
                    failure_reason.setdefault(license_en, []).append('/'.join(failure_field))
    if failed_count == 0:
        failure_reason_str = ''
    else:
        failure_reason_str = json.dumps(failure_reason)
    return compare_result, total_fields, failed_count, successful_at_this_level, failure_reason_str


def se_result_detect(ocr_res_dict):
    detect_list = []
    for license_en in consts.SE_DETECT_CARD:
        ocr_field, _, _ = consts.SE_COMPARE_FIELD[license_en]
        ocr_res_str = ocr_res_dict.get(ocr_field)
        detect_list.append(ocr_res_str is None)
    return detect_list


def se_compare(application_id, application_entity, ocr_res_id, last_obj, ocr_res_dict, is_cms):
    try:
        # 比对逻辑
        start_time = datetime.now()
        detect_list = se_result_detect(ocr_res_dict)
        if is_cms:
            last_obj = rebuild_compare_info(last_obj)
        compare_info, is_gsyh = get_se_compare_info(last_obj, application_entity, detect_list)
        compare_result, total_fields, failed_count, successful_at_this_level, failure_reason_str = se_compare_process(
            compare_info, ocr_res_dict, is_gsyh)
        compare_log.info('{0} [SE] [compare success] [entity={1}] [id={2}] [ocr_res_id={3}] [result={4}]'.format(
            log_base, application_entity, application_id, ocr_res_id, compare_result))
    except Exception as e:
        compare_log.error('{0} [SE] [compare error] [entity={1}] [id={2}] [ocr_res_id={3}] '
                          '[error={4}]'.format(log_base, application_entity, application_id, ocr_res_id,
                                               traceback.format_exc()))
    else:
        # 将比对结果写入数据库
        try:
            result_table = HILSECompareResult if application_entity == consts.HIL_PREFIX else AFCSECompareResult
            res_obj = result_table.objects.filter(application_id=application_id).first()
            if res_obj is None:
                res_obj = result_table()
                res_obj.application_id = application_id
            res_obj.compare_count = total_fields
            res_obj.failed_count = failed_count
            res_obj.is_finish = successful_at_this_level
            res_obj.version = '{0}{1}{2}'.format(consts.INFO_SOURCE[0], consts.SPLIT_STR, last_obj.application_version)
            # res_obj.reason1_count = reason1_count
            res_obj.result = json.dumps(compare_result)
            res_obj.update_time = datetime.now()
            res_obj.save()
            compare_log.info('{0} [SE] [result save success] [entity={1}] [id={2}] [ocr_res_id={3}]'.format(
                log_base, application_entity, application_id, ocr_res_id))
        except Exception as e:
            compare_log.error('{0} [SE] [result save error] [entity={1}] [id={2}] [ocr_res_id={3}] '
                              '[error={4}]'.format(log_base, application_entity, application_id, ocr_res_id,
                                                   traceback.format_exc()))

        # report
        end_time = datetime.now()
        try:
            request_trigger = RequestTrigger.SUBMITING.value if ocr_res_id is None else RequestTrigger.UPLOADING.value
            report_class = HILCompareReport if application_entity == consts.HIL_PREFIX else AFCCompareReport
            report_class.objects.create(
                case_number=application_id,
                request_team=RequestTeam.SETTLEMENT.value,
                request_trigger=request_trigger,
                transaction_start=start_time,
                transaction_end=end_time,
                successful_at_this_level=successful_at_this_level,
                failure_reason=failure_reason_str,
                process_name=ProcessName.SE_CACOMPARE.value,
                total_fields=total_fields,
                workflow_name=last_obj.customer_type,
            )
            compare_log.info('{0} [SE] [report save success] [entity={1}] [id={2}] [ocr_res_id={3}]'.format(
                    log_base, application_entity, application_id, ocr_res_id))
        except Exception as e:
            compare_log.error('{0} [SE] [report save error] [entity={1}] [id={2}] [ocr_res_id={3}] '
                              '[error={4}]'.format(log_base, application_entity, application_id, ocr_res_id,
                                                   traceback.format_exc()))

        # cms结果发送
        if is_cms:
            try:
                application_link = '{0}/showList/showList?entity={1}&scheme={2}&case_id={3}'.format(
                    conf.BASE_URL, application_entity, consts.COMPARE_DOC_SCHEME_LIST[1], application_id)
                data = {
                    "SubtenantId": consts.TENANT_MAP[application_entity],
                    "Data": {
                        "Result_Message": "Pass" if successful_at_this_level else "Fail",
                        "Failure_Reason": failure_reason_str,
                        "Application_Number": application_id,
                        "Bank_Statement": "",
                        "Link_URL": application_link,
                        "OCR_Version": 1,
                        "Origin": consts.INFO_SOURCE[1]
                    }
                }
                response = cms.send(data)
            except Exception as e:
                compare_log.error('{0} [SE] [cms error] [entity={1}] [id={2}] [ocr_res_id={3}] '
                                  '[error={4}]'.format(log_base, application_entity, application_id, ocr_res_id,
                                                       traceback.format_exc()))
            else:
                compare_log.info('{0} [SE] [cms success] [entity={1}] [id={2}] [ocr_res_id={3}] [response={4}]'.format(
                    log_base, application_entity, application_id, ocr_res_id, response))

@app.task
def compare(application_id, application_entity, uniq_seq, ocr_res_id, is_ca=True, is_cms=False):
    # POS: application_id, application_entity, uniq_seq, None
    # OCR: application_id, business_type(application_entity), None, ocr_res_id

    compare_log.info('{0} [receive task] [entity={1}] [id={2}] [uniq_seq={3}] [ocr_res_id={4}] [is_ca={5}] '
                     '[is_cms={6}]'.format(log_base, application_entity, application_id, uniq_seq, ocr_res_id,
                                           is_ca, is_cms))

    # 根据application_id查找最新的比对信息,如果没有,结束
    if is_ca:
        comparison_class = HILComparisonInfo if application_entity == consts.HIL_PREFIX else AFCComparisonInfo
    else:
        comparison_class = HILSEComparisonInfo if application_entity == consts.HIL_PREFIX else AFCSEComparisonInfo
    last_obj = comparison_class.objects.filter(application_id=application_id).last()
    if last_obj is None:
        compare_log.info('{0} [comparison info empty] [entity={1}] [id={2}] [uniq_seq={3}] [ocr_res_id={4}] '
                         '[is_ca={5}] [is_cms]={6}'.format(log_base, application_entity, application_id, uniq_seq,
                                                           ocr_res_id, is_ca, is_cms))
        return

    # 根据application_id查找OCR累计结果指定license字段,如果没有,结束
    if is_ca:
        result_class = HILOCRResult if application_entity == consts.HIL_PREFIX else AFCOCRResult
    else:
        result_class = HILSEOCRResult if application_entity == consts.HIL_PREFIX else AFCSEOCRResult
    if ocr_res_id is None:
        ocr_res_dict = result_class.objects.filter(application_id=application_id).values(*consts.COMPARE_FIELDS).first()
    else:
        ocr_res_dict = result_class.objects.filter(id=ocr_res_id).values(*consts.COMPARE_FIELDS).first()
    if ocr_res_dict is None:
        compare_log.info('{0} [ocr info empty] [entity={1}] [id={2}] [uniq_seq={3}] [ocr_res_id={4}] '
                         '[is_ca={5}] [is_cms]={6}'.format(log_base, application_entity, application_id,
                                                           uniq_seq, ocr_res_id, is_ca, is_cms))
        return

    if is_ca:
        ca_compare(application_id, application_entity, ocr_res_id, last_obj, ocr_res_dict)
    else:
        se_compare(application_id, application_entity, ocr_res_id, last_obj, ocr_res_dict, is_cms)