import os
import io
import time
import json
import random
import datetime
import traceback
import fitz
import shutil
import requests
from openpyxl import Workbook
from django.utils import timezone
# from django.http import HttpResponse
from django.db.models import Q
from rest_framework.permissions import IsAuthenticated
from webargs import fields, validate
from webargs.djangoparser import use_args, parser
from settings import conf
from common import response
from common.mixins import GenericView
from common.tools.file_tools import file_write
from common.redis_cache import redis_handler as rh
from .models import (
    # UploadDocRecords,
    DocStatus,
    PriorityApplication,
    GCAPRecords,
    AFCComparisonInfo,
    # AFCSEComparisonInfo,
    AFCSECMSInfo,
    HILComparisonInfo,
    # HILSEComparisonInfo,
    HILSECMSInfo,
    AFCCompareOfflineReport,
    HILCompareOfflineReport,
    AFCCACompareResult,
    AFCSECompareResult,
    HILCACompareResult,
    HILSECompareResult,
    AFCCACompareResultRecord,
    AFCSECompareResultRecord,
    HILCACompareResultRecord,
    HILSECompareResultRecord,
    HILAutoSettlement,
    AFCAutoSettlement,
    HILbankVerification,
    AFCbankVerification,
    MposReport,
    GenericOCRReport,
    InterfaceReport,
)
from .named_enum import ErrorType, AutoResult, WholeResult, RPAResult, SystemName
from .mixins import DocHandler, MPOSHandler, PreSEHandler
from . import consts
from apps.account.authentication import OAuth2AuthenticationWithUser
from celery_compare.tasks import compare


class CustomDate(fields.Date):

    def _deserialize(self, value, attr, data, **kwargs):
        return value


class CustomDecimal(fields.Decimal):

    def __init__(self, places_2=False, *args, **kwargs):
        self.places_2 = places_2
        super().__init__(*args, **kwargs)

    def _deserialize(self, value, attr, data, **kwargs):
        if self.places_2:
            return format(self._validated(value), ".2f")
        else:
            return format(self._validated(value), "f")
        # return self._to_string(self._validated(value))


# restframework将request.body封装至request.data, webargs从request.data中获取参数
@parser.location_loader("data")
def load_data(request, schema):
    return request.data


go_args = {
    'image': fields.Raw(required=True),
}

usedcar_args = {
    'vinNo': fields.Str(required=True, validate=validate.Length(max=128)),
    "manufactureDate": CustomDate(required=True),
    "firstRegistrationDate": CustomDate(required=True),
}

se_vehicle_args = {
    'vehicleStatus': fields.Str(required=True, validate=validate.Length(max=16)),
    'vehicleTransactionAmount': CustomDecimal(required=True),
    'vinNo': fields.Str(required=True, validate=validate.Length(max=256)),
    'dealer': fields.Str(required=True, validate=validate.Length(max=256)),
    'showRoom': fields.Str(required=False),
    'agencyDealer': fields.Str(required=False),
    'option': CustomDecimal(required=False),
    'msrp': CustomDecimal(required=False),
    'totalAmount': CustomDecimal(required=False),
}

se_insurance_args = {
    'insuredAmount': CustomDecimal(required=True),
    'insuranceType': fields.Str(required=True, validate=validate.Length(max=16)),
    'startDate': CustomDate(required=True),
    'endDate': CustomDate(required=True),
}

se_associated_args = {
    'service': fields.Str(required=True),
    'amount': CustomDecimal(required=True),
    'financedAmount': CustomDecimal(required=True)
}

se_payment_args = {
    'term': fields.Int(required=True),
    'amount': CustomDecimal(required=True, places_2=True)
}

se_bank_args = {
    'bankName': fields.Str(required=True, validate=validate.Length(max=256)),
    'branchName': fields.Str(required=True, validate=validate.Length(max=256)),
    'applicantType': fields.Str(required=True, validate=validate.Length(max=16)),
    'accountHolderName': fields.Str(required=True, validate=validate.Length(max=256)),
    'accountNo': fields.Str(required=True, validate=validate.Length(max=256)),
    'bankVerificationStatus': fields.Str(required=False, validate=validate.Length(max=128)),
    'isAllDocUploaded': fields.Boolean(required=False)
}

se_quotationt_args = {
    'totalLoanAmount': CustomDecimal(required=True),
    'loanTerm': fields.Int(required=True),
    'vehiclePrincipal': CustomDecimal(required=True),
    'associatedServicePrincipal': CustomDecimal(required=False),
    'mortgageType': fields.Str(required=True, validate=validate.Length(max=16)),
    'associatedServiceInfo': fields.List(fields.Nested(se_associated_args), required=False),
    'monthlyPaymentInfo': fields.List(fields.Nested(se_payment_args), required=True, validate=validate.Length(min=1)),
}

corporate_args = {
    'customerType': fields.Str(required=True, validate=validate.OneOf(consts.CUSTOMER_TYPE)),
    'customerChineseName': fields.Str(required=True, validate=validate.Length(max=256)),
    'legalRepName': fields.Str(required=True, validate=validate.Length(max=64)),
    'idNum': fields.Str(required=True, validate=validate.Length(max=256)),
    'businessLicenseNo': fields.Str(required=True, validate=validate.Length(max=256)),
    'taxRegistrationCode': fields.Str(required=True, validate=validate.Length(max=256)),
    "incorporationDate": CustomDate(required=True),
    "businessLicenseDueDate": CustomDate(required=True),
    "capitalRegAmount": CustomDecimal(required=True),  # TODO 2位小数限制
}

se_corporate_args = {
    'customerType': fields.Str(required=True, validate=validate.OneOf(consts.CUSTOMER_TYPE)),
    'companyName': fields.Str(required=True, validate=validate.Length(max=256)),
    'firstIdType': fields.Str(required=True, validate=validate.Length(max=16)),
    'firstIdNo': fields.Str(required=True, validate=validate.Length(max=256)),
    'businessLicenseNo': fields.Str(required=True, validate=validate.Length(max=256)),
    'organizationCreditCode': fields.Str(required=True, validate=validate.Length(max=256)),
    'taxRegistrationCertificateNo': fields.Str(required=True, validate=validate.Length(max=256)),
    "establishmentDate": CustomDate(required=True),
    "incorporationDate": CustomDate(required=True),
    "businessLicenseDueDate": CustomDate(required=True),
    'legalRepName': fields.Str(required=True, validate=validate.Length(max=64)),
    'organizationType': fields.Str(required=True, validate=validate.Length(max=16)),

    'fleetCustomer': fields.Boolean(required=False),
    'beneficialOwnerName': fields.Str(required=False, validate=validate.Length(max=64)),
    'beneficialOwnerIdType': fields.Str(required=False, validate=validate.Length(max=16)),
    'beneficialOwnerIdNo': fields.Str(required=False, validate=validate.Length(max=256)),
    'beneficialOwnerIdExpiryDate': CustomDate(required=False),
}

individual_args = {
    'applicantType': fields.Str(required=True, validate=validate.OneOf(consts.APPLICANT_TYPE)),
    'customerType': fields.Str(required=True, validate=validate.OneOf(consts.CUSTOMER_TYPE)),
    'idType': fields.Str(required=True, validate=validate.OneOf(consts.ID_TYPE)),
    'secondIdType': fields.Str(required=False, validate=validate.OneOf(consts.SECOND_ID_TYPE)),
    'customerChineseName': fields.Str(required=True, validate=validate.Length(max=64)),
    'idNum': fields.Str(required=True, validate=validate.Length(max=256)),
    'secondIdNum': fields.Str(required=False, validate=validate.Length(max=256)),
    "idExpiryDate": CustomDate(required=True),
    "dateOfBirth": CustomDate(required=True),
    'companyName': fields.Str(required=False, validate=validate.Length(max=256)),
    "registeredCapital": CustomDecimal(required=False),
    'selfEmployedSubType': fields.Str(required=False, validate=validate.OneOf(consts.SUB_TYPE)),
}

se_individual_args = {
    'customerType': fields.Str(required=True, validate=validate.OneOf(consts.CUSTOMER_TYPE)),
    'applicantType': fields.Str(required=True, validate=validate.OneOf(consts.APPLICANT_TYPE)),
    'customerName': fields.Str(required=True, validate=validate.Length(max=64)),
    'idType': fields.Str(required=True, validate=validate.OneOf(consts.ID_TYPE)),
    'idNum': fields.Str(required=True, validate=validate.Length(max=256)),
    'secondIdType': fields.Str(required=False, validate=validate.OneOf(consts.SECOND_ID_TYPE)),
    'secondIdNum': fields.Str(required=False, validate=validate.Length(max=256)),
    "dateOfBirth": CustomDate(required=True),
    "idExpiryDate": CustomDate(required=True),
    'nationality': fields.Str(required=False, validate=validate.Length(max=64)),
    'countryregion': fields.Str(required=False, validate=validate.Length(max=64)),
    'hukouProvince': fields.Str(required=False, validate=validate.Length(max=64)),
    'hukouCity': fields.Str(required=False, validate=validate.Length(max=64)),
    'residentialProvince': fields.Str(required=False, validate=validate.Length(max=64)),
    'residentialCity': fields.Str(required=False, validate=validate.Length(max=64)),
    'companyName': fields.Str(required=False, validate=validate.Length(max=256)),
    'registeredCapital': CustomDecimal(required=False),
    'selfEmployedSubType': fields.Str(required=False, validate=validate.Length(max=16)),
}

comment_args = {
    'comment': fields.Str(required=True, validate=validate.Length(max=1024)),
}

compare_content = {
    'uniqSeq': fields.Str(required=True, validate=validate.Length(max=128)),
    'applicationId': fields.Str(required=True, validate=validate.Length(max=64)),
    'applicationEntity': fields.Str(required=True, validate=validate.OneOf(consts.ENTITY)),
    'customerType': fields.Str(required=True, validate=validate.OneOf(consts.CUSTOMER_TYPE)),
    "applicationVersion": fields.Int(required=True),
    'vehicleStatus': fields.Str(required=True, validate=validate.OneOf(consts.VEHICLE_STATUS)),

    'comments': fields.List(fields.Nested(comment_args), required=True),

    'individualCusInfo': fields.List(fields.Nested(individual_args),
                                     required=True, validate=validate.Length(min=1, max=4)),

    'usedCarInfo': fields.Nested(usedcar_args, required=False),
    'corporateCusInfo': fields.Nested(corporate_args, required=False),
}

se_compare_content = {
    'uniqSeq': fields.Str(required=True, validate=validate.Length(max=128)),
    'applicationId': fields.Str(required=True, validate=validate.Length(max=64)),
    "applicationVersion": fields.Int(required=True),
    'applicationEntity': fields.Str(required=True, validate=validate.OneOf(consts.ENTITY)),

    'productGroup': fields.Str(required=False),
    'productGroupID': fields.Str(required=False),

    'customerType': fields.Str(required=True, validate=validate.OneOf(consts.CUSTOMER_TYPE)),
    "firstSubmmisonDate": CustomDate(required=True),
    'propertyDocumentPolicy': fields.Str(required=False, validate=validate.Length(max=16)),
    'isAutoSettlement': fields.Boolean(required=False),

    'individualCusInfo': fields.List(fields.Nested(se_individual_args),
                                     required=True, validate=validate.Length(min=1, max=4)),
    'corporateCusInfo': fields.Nested(se_corporate_args, required=False),
    'vehicleInfo': fields.Nested(se_vehicle_args, required=True),
    'insuranceInfo': fields.Nested(se_insurance_args, required=True),
    'bankInfo': fields.Nested(se_bank_args, required=True),
    'quotationtInfo': fields.Nested(se_quotationt_args, required=True),
}

compare_args = {
    'content': fields.Nested(compare_content, required=True)
}

se_compare_args = {
    'content': fields.Nested(se_compare_content, required=True)
}

application_data_args = {
    'applicationId': fields.Str(required=True, validate=validate.Length(max=64)),
    'applicationStatus': fields.Str(required=False, validate=validate.Length(max=64)),
}

applicant_data_args = {
    # 'mainApplicantName': fields.Str(required=True, validate=validate.Length(max=16)),
    # 'coApplicantName': fields.Str(required=True, validate=validate.Length(max=16)),
    # 'guarantor1Name': fields.Str(required=True, validate=validate.Length(max=16)),
    # 'guarantor2Name': fields.Str(required=True, validate=validate.Length(max=16)),
    'mainApplicantName': fields.Str(required=False),
    'coApplicantName': fields.Str(required=False),
    'guarantor1Name': fields.Str(required=False),
    'guarantor2Name': fields.Str(required=False),
}

document_args = {
    'documentName': fields.Str(required=True, validate=validate.Length(max=255)),
    # Acceptance/Settlement/Contract Management
    'documentScheme': fields.Str(required=True, validate=validate.Length(max=64)),
    'businessType': fields.Str(required=True, validate=validate.Length(max=64)),    # CO00001/CO00002
    'uploadFinishTime': fields.DateTime(required=True),
    'dataSource': fields.Str(required=True, validate=validate.Length(max=64)),  # POS/EAPP/Econtract
    'metadataVersionId': fields.Str(required=True, validate=validate.Length(max=64)),
    'password': fields.Str(required=False, validate=validate.Length(max=16)),
}

doc_upload_args = {
    'applicationData': fields.Nested(application_data_args, required=True),
    'applicantData': fields.Nested(applicant_data_args, required=False),
    'document': fields.Nested(document_args, required=True),
}

doc_list_args = {
    'page': fields.Int(required=False,
                       missing=consts.PAGE_DEFAULT,
                       validate=lambda val: val >= 1),
    'page_size': fields.Int(required=False,
                            missing=consts.PAGE_SIZE_DEFAULT,
                            validate=lambda val: val >= 1),
    'status': fields.Int(required=False,
                         validate=validate.OneOf(DocStatus.get_value_lst())),
    'application_id': fields.Str(required=False, validate=validate.Length(max=64)),
    'data_source': fields.Str(required=False, validate=validate.OneOf(consts.DATA_SOURCE_LIST)),
    'business_type': fields.Str(required=True, validate=validate.OneOf(consts.BUSINESS_TYPE_LIST)),
    'upload_time_start': fields.Date(required=False),
    'upload_time_end': fields.Date(required=False),
    'create_time_start': fields.Date(required=False),
    'create_time_end': fields.Date(required=False),
}

auto_list_args = {
    'page': fields.Int(required=False,
                       missing=consts.PAGE_DEFAULT,
                       validate=lambda val: val >= 1),
    'page_size': fields.Int(required=False,
                            missing=consts.PAGE_SIZE_DEFAULT,
                            validate=lambda val: val >= 1),
    'business_type': fields.Str(required=True, validate=validate.OneOf(consts.BUSINESS_TYPE_LIST)),
    'application_id': fields.Str(required=False, validate=validate.Length(max=64)),
    'auto_result': fields.Int(required=False,
                              validate=validate.OneOf(AutoResult.get_value_lst())),
    'whole_result': fields.Int(required=False,
                               validate=validate.OneOf(WholeResult.get_value_lst())),
    'rpa_result': fields.Int(required=False,
                             validate=validate.OneOf(RPAResult.get_value_lst())),
    'get_case_from_ocr_time_start': fields.Date(required=False),
    'get_case_from_ocr_time_end': fields.Date(required=False),
    'activated_time_start': fields.Date(required=False),
    'activated_time_end': fields.Date(required=False),
    'comparison_time_start': fields.Date(required=False),
    'comparison_time_end': fields.Date(required=False),
}

compare_result_args = {
    'entity': fields.Str(required=True, validate=validate.OneOf(consts.BUSINESS_TYPE_LIST)),
    'scheme': fields.Str(required=True, validate=validate.OneOf(consts.COMPARE_DOC_SCHEME_LIST)),
    'id': fields.Int(required=False, validate=lambda val: val >= 1),
    'case_id': fields.Str(required=True, validate=validate.Length(max=64)),
    'auto': fields.Int(required=False),
}

upload_pdf_args = {
    'pdf_file': fields.Raw(required=True),
}

application_information = {
    "SUBMIT_DATETIME": fields.DateTime(required=True),
    "STATUS": fields.Int(required=True),
    'ENTITY': fields.Str(required=True, validate=validate.Length(max=100)),
    "RATING": fields.Int(required=True),
    "APPLICATION_ID": fields.Str(required=True, validate=validate.Length(max=100)),
    "APPLICATION_VERSION": fields.Int(required=True),
    "INTERMEDIATE_DECISION": fields.Int(required=True),
}

priority_doc_args = {
    'APPLICATION_INFORMATION': fields.Nested(application_information, required=True)
}

compare_offline_args = {
    'is_hil': fields.Boolean(required=True),
    'case_number': fields.Str(required=True, validate=validate.Length(max=255)),
    'request_team': fields.Str(required=True, validate=validate.Length(max=255)),
    'request_trigger': fields.Str(required=True, validate=validate.Length(max=1024)),
    'input_file': fields.Str(required=True, validate=validate.Length(max=2048)),
    'transaction_start': fields.DateTime(required=True),
    'transaction_end': fields.DateTime(required=True),
    'successful_at_this_level': fields.Boolean(required=True),
    'failure_reason': fields.Str(required=False),
    'process_name': fields.Str(required=True, validate=validate.Length(max=1024)),
    'total_fields': fields.Int(required=True),
    'workflow_name': fields.Str(required=True, validate=validate.Length(max=1024)),
}

id_info_args = {
    'idType': fields.Str(required=True, validate=validate.Length(max=32)),
    'idNum': fields.Str(required=True, validate=validate.Length(max=64)),
    'idExpiryDate': CustomDate(required=True),
}

info_args = {
    'applicantType': fields.Str(required=True, validate=validate.Length(max=64)),
    'customersubType': fields.Str(required=True, validate=validate.Length(max=32)),
    'selfEmployedSubType': fields.Str(required=False, validate=validate.Length(max=32)),
    'name': fields.Str(required=True, validate=validate.Length(max=64)),
    'legalRepName': fields.Str(required=False, validate=validate.Length(max=64)),
    'dateOfBirth': CustomDate(required=False),
    'nationality': fields.Str(required=False, validate=validate.Length(max=64)),
    'establishmentDate': CustomDate(required=False),
    'IDInformation': fields.List(fields.Nested(id_info_args), required=True,
                                 validate=validate.Length(min=1)),
}

auto_approve_details = {
    'aaType': fields.Str(required=False, validate=validate.Length(max=64)),
    'policyComments': fields.Str(required=False),
}

financial_info = {
    'vehiclePrice': CustomDecimal(required=True),
    'grossPrice': CustomDecimal(required=True),
    'associatedServicePrice': CustomDecimal(required=True),
    'vehiclePrincipal': CustomDecimal(required=True),
    'associatedServicePrincipal': CustomDecimal(required=True),
    'originationPrincipal': CustomDecimal(required=True),
    'totalDownPayment': CustomDecimal(required=True),
    'vehicleDownPaymentRatio': CustomDecimal(required=True),
    'optionAmount': CustomDecimal(required=True),
    'sumOfMSRPAndOption': CustomDecimal(required=True),
}

payment_schedule = {
    'no': fields.Int(required=True),
    'grossRentalAmount': CustomDecimal(required=True),
}

associated_services = {
    'associatedServices': fields.Str(required=False, validate=validate.Length(max=64)),
    'price': CustomDecimal(required=False),
    'financed': CustomDecimal(required=False),
    'total': CustomDecimal(required=False),
}

vehicle_info = {
    'vinNo': fields.Str(required=True, validate=validate.Length(max=64)),
}

bank_details = {
    'bankName': fields.Str(required=True, validate=validate.Length(max=64)),
    'accountHolderName': fields.Str(required=True, validate=validate.Length(max=64)),
    'accountNo': fields.Str(required=True, validate=validate.Length(max=64)),
}

insurance_details = {
    'insuranceType': fields.Str(required=True, validate=validate.Length(max=64)),
    'insuranceAmount': fields.Str(required=True, validate=validate.Length(max=64)),
    'startDate': CustomDate(required=True),
    'endDate': CustomDate(required=True),
}

corporate_info = {
    'hashCode': fields.Str(required=False, validate=validate.Length(max=64)),
    'borrowerName': fields.Str(required=False, validate=validate.Length(max=64)),
    'fiscalYear': fields.Int(required=False),
    'totaAssets': CustomDecimal(required=False),
    'totalLiabilitiesAndOwnersEquity': CustomDecimal(required=False),
    'cashAndCashEquivalentAtEndOfPeriod': CustomDecimal(required=False),
    'netProfit': CustomDecimal(required=False),
}

se_verification = {
    'applicationNo': fields.Str(required=True, validate=validate.Length(max=64)),
}

se_cms_content = {
    'financeCompany': fields.Str(required=True, validate=validate.Length(max=512)),
    'contractNo': fields.Str(required=True, validate=validate.Length(max=64)),
    'status': fields.Str(required=True, validate=validate.Length(max=64)),
    'branch': fields.Str(required=True, validate=validate.Length(max=512)),
    'fpCampaign': fields.Str(required=True, validate=validate.Length(max=512)),
    'applicationVersion': fields.Int(required=True),
    'submissionDate': CustomDate(required=True),
    'mortgageType': fields.Str(required=True, validate=validate.Length(max=64)),
    'dealerRegion': fields.Str(required=True, validate=validate.Length(max=64)),
    'insuranceRealNameCity': fields.Boolean(required=True),
    'totalFinanceAmount': CustomDecimal(required=True),
    'terms': fields.Int(required=True),
    'dealerName': fields.Str(required=True, validate=validate.Length(max=512)),
    'tier': fields.Str(required=True, validate=validate.Length(max=64)),
    'province': fields.Str(required=True, validate=validate.Length(max=64)),
    'fapiaoIssuerDealer': fields.Str(required=False, validate=validate.Length(max=512)),
    'customerName': fields.Str(required=True, validate=validate.Length(max=64)),
    'customerIdNo': fields.Str(required=True, validate=validate.Length(max=64)),
    'vehicleStatus': fields.Str(required=True, validate=validate.Length(max=64)),
    'applicationSource': fields.Str(required=True, validate=validate.Length(max=64)),
    'contractSource': fields.Str(required=True, validate=validate.Length(max=64)),
    'applicationRating': fields.Int(required=False),

    'applicantInformation': fields.List(fields.Nested(info_args),
                                        required=True, validate=validate.Length(min=1, max=4)),
    'autoApprovedDetails': fields.Nested(auto_approve_details, required=True),
    'financialInformation': fields.Nested(financial_info, required=True),
    'paymentSchedule': fields.List(fields.Nested(payment_schedule), required=True,
                                   validate=validate.Length(min=1)),
    'associatedServices': fields.List(fields.Nested(associated_services), required=True),
    'vehicleInformation': fields.Nested(vehicle_info, required=True),
    'bankAccountDetails': fields.Nested(bank_details, required=True),
    'insuranceDetails': fields.Nested(insurance_details, required=True),
    'corporateFinancialInformation': fields.Nested(corporate_info, required=True),
    'settlemnetVerification': fields.Nested(se_verification, required=True),
}

se_cms_args = {
    'content': fields.Nested(se_cms_content, required=True)
}

se_contract_content = {
    'uniqSeq': fields.Str(required=True, validate=validate.Length(max=128)),
    'applicationId': fields.Str(required=True, validate=validate.Length(max=64)),
    'applicationEntity': fields.Str(required=True, validate=validate.OneOf(consts.ENTITY)),
    "applicationVersion": fields.Int(required=True),
}

se_contract_args = {
    'content': fields.Nested(se_contract_content, required=True)
}

result_item_args = {
    consts.HEAD_LIST[0]: fields.Str(required=True),
    consts.HEAD_LIST[1]: fields.Str(required=True),
    consts.HEAD_LIST[2]: fields.Str(required=True),
    consts.HEAD_LIST[3]: fields.Str(required=True),
    consts.HEAD_LIST[4]: fields.Str(required=True),
    consts.HEAD_LIST[5]: fields.Str(required=True),
    consts.HEAD_LIST[6]: fields.Str(required=True),
    consts.HEAD_LIST[7]: fields.Str(required=True),
    consts.HEAD_LIST[8]: fields.Str(required=True),
    consts.HEAD_LIST[9]: fields.Int(required=True),
}

result_update_args = {
    'id': fields.Int(required=True),
    'application_id': fields.Str(required=True, validate=validate.Length(max=64)),
    'entity': fields.Str(required=True, validate=validate.OneOf(consts.BUSINESS_TYPE_LIST)),
    'scheme': fields.Str(required=True, validate=validate.OneOf(consts.DOC_SCHEME_LIST)),
    'whole_result': fields.Str(required=True),
    'version': fields.Str(required=True),
    'source': fields.Str(required=True),
    'latest_compared_time': fields.Str(required=True),
    'comments': fields.Str(required=True),
    'result': fields.List(fields.Nested(result_item_args), required=True, validate=validate.Length(min=1)),
}

mpos_args = {
    'type': fields.Int(required=True, validate=validate.OneOf(consts.MPOS_MAP)),
    'file_base64_content': fields.List(fields.Str(), required=True, validate=validate.Length(min=1)),
}


class UploadDocView(GenericView, DocHandler):
    # permission_classes = []
    # authentication_classes = []
    permission_classes = [IsAuthenticated]
    authentication_classes = [OAuth2AuthenticationWithUser]
    # required_scopes = ['write']

    # 上传(接收)文件接口
    @use_args(doc_upload_args, location='data')
    def post(self, request, args):  # interface_report pos/eapp/econtract to ocr
        start_time = time.time()

        application_data = args.get('applicationData')
        # applicant_data = args.get('applicantData')
        document = args.get('document')
        business_type = document.get('businessType')
        application_id = application_data.get('applicationId')
        document_scheme = document.get('documentScheme')
        data_source = document.get('dataSource')
        document_name = document.get('documentName', '')
        pwd = document.get('password', '')

        data_source = self.fix_data_source(data_source)
        document_scheme = self.fix_scheme(document_scheme)
        
        if data_source == consts.DATA_SOURCE_LIST[1]:
            if document_name.endswith('-证书.pdf') or document_name.endswith('-证书'):
                self.running_log.info('[doc upload success] [eapp license skip] [args={0}]'.format(args))
                return response.ok()

        # 2. 根据业务类型分库存储
        doc_class, prefix = self.get_doc_class(business_type)
        doc = doc_class.objects.create(
            metadata_version_id=document.get('metadataVersionId'),
            application_id=application_id,
            document_name=document_name,
            document_scheme=document_scheme,
            data_source=data_source,
            upload_finish_time=document.get('uploadFinishTime'),
            password=pwd if isinstance(pwd, str) and len(pwd) > 0 else None,
        )

        # 3. 选择队列进入
        is_priority = PriorityApplication.objects.filter(application_id=application_id, on_off=True).exists()
        is_zip = False

        classify_1 = 0
        # 电子合同
        if data_source == consts.DATA_SOURCE_LIST[-1] and document_scheme == consts.DOC_SCHEME_LIST[1]:
            for keyword, classify_1_tmp in consts.ECONTRACT_KEYWORDS_MAP.get(prefix):
                if keyword in document_name:
                    classify_1 = classify_1_tmp
                    break
        elif document_name.endswith('.zip') or document_name.endswith('.rar') or document_name.endswith('.ZIP') \
                or document_name.endswith('.RAR'):
            is_zip = True

        task = consts.SPLIT_STR.join([prefix, str(doc.id), str(classify_1)])
        enqueue_res = rh.enqueue([task], is_priority, is_zip)
        self.running_log.info('[doc upload success] [args={0}] [business_type={1}] [doc_id={2}] '
                              '[is_priority={3}] [enqueue_res={4}]'.format(args, prefix, doc.id,
                                                                           is_priority, enqueue_res))

        try:
            end_time = time.time()
            document.pop('uploadFinishTime', None)
            duration_second = int(end_time - start_time)
            InterfaceReport.objects.create(
                source=data_source,
                target=SystemName.OCR.name,
                body=json.dumps(args),
                response=None,
                status=True,
                # retry_times=None,
                duration=duration_second,
            )
        except Exception as e:
            self.exception_log.exception('[upload view] [db save failed] [error={0}]'.format(traceback.format_exc()))

        return response.ok()

    post.openapi_doc = '''
    tags: [doc]
    summary: POS系统上传文件信息
    consumes: [application/json]
    produces: [application/json]
    parameters:
    - in: body
      name: body
      required: true
      schema:
        $ref: "#/definitions/Doc"

    responses:
      200:
        description: ok
        schema:
          $ref: '#/definitions/ApiResponse'
    '''


class PriorityDocView(GenericView, DocHandler):
    permission_classes = [IsAuthenticated]
    authentication_classes = [OAuth2AuthenticationWithUser]

    # 优先级订单接口
    @use_args(priority_doc_args, location='data')
    def post(self, request, args):  # interface_report gcap to ocr
        start_time = time.time()

        application_info = args.get('APPLICATION_INFORMATION')
        application_id = application_info.get('APPLICATION_ID')
        submit_datetime = application_info.get('SUBMIT_DATETIME')
        intermediate_decision = str(application_info.get('INTERMEDIATE_DECISION'))
        entity = application_info.get('ENTITY')
        if submit_datetime.utcoffset() is not None:
            submit_datetime = timezone.make_naive(submit_datetime, timezone.get_current_timezone())
        GCAPRecords.objects.create(
            entity=entity,
            status=application_info.get('STATUS'),
            rating=application_info.get('RATING'),
            application_id=application_id,
            application_version=application_info.get('APPLICATION_VERSION'),
            intermediate_decision=intermediate_decision,
            submit_datetime=submit_datetime,
        )

        if intermediate_decision not in consts.PRIORITY_WORDS:
            self.running_log.info('[priority doc skip] [args={0}]'.format(args))
            return response.ok()

        _, created = PriorityApplication.objects.update_or_create(application_id=application_id,
                                                                  defaults={'on_off': True})
        if created:
            doc_class, prefix = self.get_doc_class(entity)
            doc_ids = doc_class.objects.filter(application_id=application_id,
                                               status=DocStatus.INIT.value).values_list('id', flat=True)
            tasks_list = ['{0}{1}{2}'.format(prefix, consts.SPLIT_STR, doc_id) for doc_id in doc_ids]
            if not tasks_list:
                self.running_log.info(
                    '[priority doc success] [args={0}]'.format(args))
            else:
                enqueue_res = rh.enqueue(tasks_list, is_priority=True)  # TODO 可能把压缩文件放入优先队列
                self.running_log.info('[priority doc success] [args={0}] [tasks_list={1}] [enqueue_res={2}]'.format(
                    args, tasks_list, enqueue_res))

        try:
            end_time = time.time()
            duration_second = int(end_time - start_time)
            application_info.pop('SUBMIT_DATETIME', None)
            InterfaceReport.objects.create(
                source=SystemName.GCAP.name,
                target=SystemName.OCR.name,
                body=json.dumps(args),
                response=None,
                status=True,
                # retry_times=None,
                duration=duration_second,
            )
        except Exception as e:
            self.exception_log.exception('[gcap view] [db save failed] [error={0}]'.format(traceback.format_exc()))

        return response.ok()

    post.openapi_doc = '''
        tags: [doc]
        summary: GCAP提高申请单对应文件优先级
        consumes: [application/json]
        produces: [application/json]
        parameters:
        - in: body
          name: body
          required: true
          schema:
            $ref: "#/definitions/Application"

        responses:
          200:
            description: ok
            schema:
              $ref: '#/definitions/ApiResponse'
        '''


class CompareView(GenericView):
    permission_classes = [IsAuthenticated]
    authentication_classes = [OAuth2AuthenticationWithUser]

    # pos上传比对信息接口 CA
    @use_args(compare_args, location='data')
    def post(self, request, args):  # interface_report pos to ocr
        start_time = time.time()

        # 存库
        content = args.get('content', {})
        uniq_seq = content.get('uniqSeq')
        business_type = content.get('applicationEntity')
        application_id = content.get('applicationId')
        individual_cus_info = json.dumps(content.get('individualCusInfo'))
        usedcar_info = json.dumps(content.get('usedCarInfo')) if isinstance(content.get('usedCarInfo'), dict) else None
        corporate_cus_info = json.dumps(content.get('corporateCusInfo')) if isinstance(
            content.get('corporateCusInfo'), dict) else None
        comparison_class = HILComparisonInfo if business_type in consts.HIL_SET else AFCComparisonInfo
        comparison_class.objects.create(
            uniq_seq=uniq_seq,
            application_id=application_id,
            customer_type=content.get('customerType'),
            application_version=content.get('applicationVersion'),
            vehicle_status=content.get('vehicleStatus'),
            individual_cus_info=individual_cus_info,
            usedcar_info=usedcar_info,
            corporate_cus_info=corporate_cus_info,
        )
        # 触发比对
        compare.apply_async((application_id, business_type, uniq_seq, None, True, False),
                            queue='queue_compare')

        try:
            end_time = time.time()
            duration_second = int(end_time - start_time)
            InterfaceReport.objects.create(
                source=SystemName.POS.name,
                target=SystemName.OCR.name,
                body=json.dumps(args),
                response=None,
                status=True,
                # retry_times=None,
                duration=duration_second,
            )
        except Exception as e:
            self.exception_log.exception('[pos ca view] [db save failed] [error={0}]'.format(traceback.format_exc()))

        return response.ok()

    post.openapi_doc = '''
        tags: [info]
        summary: POS上传CA比对信息
        consumes: [application/json]
        produces: [application/json]
        parameters:
        - in: body
          name: body
          required: true
          schema:
            $ref: "#/definitions/Comparison"

        responses:
          200:
            description: ok
            schema:
              $ref: '#/definitions/ApiResponse'
        '''


class SECompareView(GenericView, PreSEHandler):
    permission_classes = [IsAuthenticated]
    authentication_classes = [OAuth2AuthenticationWithUser]

    # SE preSettlement
    @use_args(se_compare_args, location='data')
    def post(self, request, args):  # interface_report pos to ocr
        start_time = time.time()
        log_base = '[prese]'
        # 存库
        content = args.get('content', {})
        business_type = content.get('applicationEntity')
        application_id = content.get('applicationId')
        uniq_seq = content.get('uniqSeq')
        bank_verify = content.get('bankInfo', {}).get('bankVerificationStatus', '')

        # 存库, 用于银行卡比对
        try:
            bank_class = HILbankVerification if business_type in consts.HIL_SET else AFCbankVerification
            bank_obj = bank_class.objects.filter(application_id=application_id).first()

            if bank_obj is None and bank_verify == 'PASS':
                bank_class.objects.create(
                    application_id=application_id,
                )
            elif bank_obj is not None and bank_verify == 'PASS' and bank_obj.on_off is False:
                bank_obj.on_off = True
                bank_obj.save()
            elif bank_obj is not None and bank_verify != 'PASS' and bank_obj.on_off is True:
                bank_obj.on_off = False
                bank_obj.save()
        except Exception as e:
            self.running_log.info('{0} [bankCard verify save db error] [applicationEntity={1}] '
                                  '[application_id={2}] [bank_status={3}] [error={4}]'.format(
                log_base, business_type, application_id, bank_verify, traceback.format_exc()))

        # preSettlement比对
        compare_result = self.pre_compare_entrance(content)
        self.running_log.info('{0} [prese completed] [applicationEntity={1}] [application_id={2}] [uniq_seq={3}] '
                              '[result={4}]'.format(log_base, business_type, application_id, uniq_seq, compare_result))

        try:
            end_time = time.time()
            duration_second = int(end_time - start_time)
            InterfaceReport.objects.create(
                source=SystemName.POS.name,
                target=SystemName.OCR.name,
                body=json.dumps(args),
                response=json.dumps(compare_result),
                status=True,
                # retry_times=None,
                duration=duration_second,
            )
        except Exception as e:
            self.exception_log.exception('[pos pre view] [db save failed] [error={0}]'.format(traceback.format_exc()))

        return response.ok(data=compare_result)

    post.openapi_doc = '''
        tags: [info]
        summary: POS上传SE比对信息
        consumes: [application/json]
        produces: [application/json]
        parameters:
        - in: body
          name: body
          required: true
          schema:
            $ref: "#/definitions/SEComparison"

        responses:
          200:
            description: ok
            schema:
              $ref: '#/definitions/ApiResponse'
        '''


class CompareOfflineView(GenericView):
    permission_classes = [IsAuthenticated]
    authentication_classes = [OAuth2AuthenticationWithUser]

    # 线下文件夹比对结果上传接口
    @use_args(compare_offline_args, location='data')
    def post(self, request, args):
        is_hil = args.get('is_hil', False)
        table = HILCompareOfflineReport if is_hil else AFCCompareOfflineReport
        table.objects.create(
            case_number=args.get('case_number'),
            request_team=args.get('request_team'),
            request_trigger=args.get('request_trigger'),
            input_file=args.get('input_file'),
            transaction_start=args.get('transaction_start'),
            transaction_end=args.get('transaction_end'),
            successful_at_this_level=args.get('successful_at_this_level'),
            failure_reason=args.get('failure_reason', ''),
            process_name=args.get('process_name'),
            total_fields=args.get('total_fields', 0),
            workflow_name=args.get('workflow_name'),
        )
        return response.ok()

    post.openapi_doc = '''
        tags: [info]
        summary: 线下文件夹比对结果上传
        consumes: [application/json]
        produces: [application/json]
        parameters:
        - in: body
          name: body
          required: true
          schema:
            $ref: "#/definitions/ComparisonOffline"

        responses:
          200:
            description: ok
            schema:
              $ref: '#/definitions/ApiResponse'
        '''


class DocView(GenericView, DocHandler):

    # 文件列表页
    @use_args(doc_list_args, location='querystring')
    def get(self, request, args):
        page = args.get('page', consts.PAGE_DEFAULT)
        page_size = args.get('page_size', consts.PAGE_SIZE_DEFAULT)
        status = args.get('status')
        application_id = args.get('application_id')
        data_source = args.get('data_source')
        business_type = args.get('business_type')
        upload_time_start = args.get('upload_time_start')
        upload_time_end = args.get('upload_time_end')
        create_time_start = args.get('create_time_start')
        create_time_end = args.get('create_time_end')

        status_query = Q(status=status) if status is not None else Q()
        application_id_query = Q(application_id__contains=application_id) if application_id is not None else Q()
        data_source_query = Q(data_source=data_source) if data_source is not None else Q()
        upload_finish_time_query = Q(upload_finish_time__gte=upload_time_start,
                                     upload_finish_time__lt=upload_time_end + datetime.timedelta(days=1))\
            if upload_time_start is not None and upload_time_end is not None else Q()
        create_time_query = Q(create_time__gte=create_time_start,
                              create_time__lt=create_time_end + datetime.timedelta(days=1))\
            if create_time_start is not None and create_time_end is not None else Q()
        query = application_id_query & status_query & data_source_query & upload_finish_time_query & create_time_query
        val_tuple = ('id', 'application_id', 'upload_finish_time', 'create_time', 'document_scheme', 'data_source',
                     'status', 'duration', 'page_count')
        doc_class, prefix = self.get_doc_class(business_type)
        total = doc_class.objects.filter(query).count()
        start_index = page_size * (page - 1)
        end_index = page_size * page
        if start_index >= total > 0:
            raise self.invalid_params('页数不存在')

        doc_queryset = doc_class.objects.filter(query).values(*val_tuple).order_by('-create_time')[start_index: end_index]
        # doc_list = self.get_doc_list(doc_queryset, prefix)
        for doc_dict in doc_queryset:
            tmp_scheme = consts.COMPARE_DOC_SCHEME_LIST[0] if doc_dict['document_scheme'] == consts.DOC_SCHEME_LIST[0]\
                else consts.COMPARE_DOC_SCHEME_LIST[1]
            application_link = '{0}/showList/showList?entity={1}&scheme={2}&case_id={3}'.format(
                conf.BASE_URL, prefix, tmp_scheme, doc_dict['application_id'])
            doc_dict['target_url'] = application_link

        # total = len(doc_list)
        pagination = {'current': page, 'total': total, 'page_size': page_size}
        res = {
            'pagination': pagination,
            'doc_list': list(doc_queryset)
        }
        # 新增scheme、处理时长、文件页数,删除下载切图
        # 新增链接跳转比对结果
        self.running_log.info('[get doc list] [args={0}] [res={1}]'.format(args, res))
        return response.ok(data=res)

    # 上传pdf,模拟下单
    @use_args(upload_pdf_args, location='files')
    def post(self, request, args):
        random_int = random.randint(0, consts.TIME_NUM)
        metadata_version_id = str(int(time.time()) - random_int)

        pdf_file = args.get('pdf_file')
        if isinstance(pdf_file.name, str):
            if not pdf_file.name.endswith('pdf') and not pdf_file.name.endswith('PDF'):
                self.invalid_params(msg='invalid params: not a PDF file')

        # business_type = random.choice(consts.BUSINESS_TYPE_LIST)
        business_type = consts.BUSINESS_TYPE_LIST[0]
        tmp_save_path = os.path.join(conf.DATA_DIR, business_type, '{0}.pdf'.format(metadata_version_id))
        file_write(pdf_file, tmp_save_path)

        try:
            file = fitz.Document(tmp_save_path)
        except Exception as e:
            os.remove(tmp_save_path)
            raise self.invalid_params(msg='invalid params: not a PDF file')
        else:
            if not file.isPDF:
                file.close()
                os.remove(tmp_save_path)
                raise self.invalid_params(msg='invalid params: not a PDF file')
            # elif not self.xss_pass(file):
            #     os.remove(tmp_save_path)
            #     raise self.invalid_params(msg='invalid params: PDF file XSS')


        file.close()
        # 1. 上传信息记录
        application_id = '{0}{1}'.format(consts.FIXED_APPLICATION_ID_PREFIX, metadata_version_id)
        upload_finish_time = timezone.now()
        # document_scheme = random.choice(consts.DOC_SCHEME_LIST)
        document_scheme = consts.DOC_SCHEME_LIST[1]
        data_source = random.choice(consts.DATA_SOURCE_LIST)
        # UploadDocRecords.objects.create(
        #     metadata_version_id=metadata_version_id,
        #     application_id=application_id,
        #     main_applicant='',
        #     co_applicant='',
        #     guarantor_1='',
        #     guarantor_2='',
        #     document_name=application_id,
        #     document_scheme=document_scheme,
        #     business_type=business_type,
        #     data_source=data_source,
        #     upload_finish_time=upload_finish_time,
        # )

        # 2. 根据业务类型分库存储
        doc_class, prefix = self.get_doc_class(business_type)
        doc = doc_class.objects.create(
            metadata_version_id=metadata_version_id,
            application_id=application_id,
            # main_applicant='',
            # co_applicant='',
            # guarantor_1='',
            # guarantor_2='',
            document_name=application_id,
            document_scheme=document_scheme,
            data_source=data_source,
            upload_finish_time=upload_finish_time,
        )

        # 3.pdf文件移动
        save_dir_path = os.path.join(conf.DATA_DIR, business_type, consts.TMP_DIR_NAME, str(doc.id))
        save_file_path = os.path.join(save_dir_path, '{0}.pdf'.format(doc.id))
        os.makedirs(save_dir_path, exist_ok=True)
        # file_write(pdf_file, save_file_path)
        shutil.move(tmp_save_path, save_file_path)

        # 4. 选择队列进入
        is_priority = False
        tasks = ['{0}{1}{2}'.format(prefix, consts.SPLIT_STR, doc.id)]
        enqueue_res = rh.enqueue(tasks, is_priority)

        self.running_log.info('[mock doc upload success] [args={0}] [business_type={1}] [doc_id={2}] '
                              '[is_priority={3}] [enqueue_res={4}]'.format(args, prefix, doc.id,
                                                                           is_priority, enqueue_res))
        data = {'excel_path': os.path.join(save_dir_path, '{0}.xlsx'.format(doc.id))}
        return response.ok(data=data)


class CompareResultView(GenericView):
    # permission_classes = []
    # authentication_classes = []
    # permission_classes = [IsAuthenticated]
    # authentication_classes = [OAuth2AuthenticationWithUser]

    # 获取比对结果
    @use_args(compare_result_args, location='querystring')
    def get(self, request, args):
        result_id = args.get('id', None)
        entity = args.get('entity')
        scheme = args.get('scheme')
        case_id = args.get('case_id')
        is_auto = args.get('auto')
        if is_auto == 1:
            result_table = HILAutoSettlement if entity == consts.HIL_PREFIX else AFCAutoSettlement

            if result_id is not None:
                result_obj = result_table.objects.filter(id=result_id).first()
            else:
                result_obj = result_table.objects.filter(application_id=case_id).first()

            if result_obj is None:
                whole_result = ''
                latest_compared_time = ''
            else:
                whole_result = consts.RESULT_Y if result_obj.ocr_auto_result_pass else consts.RESULT_N
                latest_compared_time = '' if result_obj.ocr_latest_comparison_time is None else result_obj.ocr_latest_comparison_time.strftime('%Y-%m-%d %H:%M')

            source = consts.INFO_SOURCE[1]
            version = comments = ''

            compare_result = {
                'id': 0 if result_obj is None else result_obj.id,
                'application_id': case_id,
                'entity': entity,
                'scheme': consts.DOC_SCHEME_LIST[0] if scheme == consts.COMPARE_DOC_SCHEME_LIST[0] else
                consts.DOC_SCHEME_LIST[1],
                'whole_result': whole_result,
                'latest_compared_time': latest_compared_time,
                'source': source,
                'version': version,
                'comments': comments,
                'result': [] if result_obj is None or not result_obj.ocr_auto_result else json.loads(result_obj.ocr_auto_result)
            }

            return response.ok(data=compare_result)

        if entity == consts.HIL_PREFIX:
            result_table = HILCACompareResult if scheme == consts.COMPARE_DOC_SCHEME_LIST[0] else HILSECompareResult
        else:
            result_table = AFCCACompareResult if scheme == consts.COMPARE_DOC_SCHEME_LIST[0] else AFCSECompareResult

        if result_id is not None:
            result_obj = result_table.objects.filter(id=result_id).first()
        else:
            result_obj = result_table.objects.filter(application_id=case_id).first()

        if result_obj is None:
            whole_result = ''
        else:
            whole_result = consts.RESULT_Y if result_obj.is_finish else consts.RESULT_N

        if result_obj is None or not isinstance(result_obj.comments, str):
            comments = ''
        else:
            comments = result_obj.comments

        if result_obj is None or not isinstance(result_obj.version, str):
            source = ''
            version = ''
        else:
            source, version = result_obj.version.split(consts.SPLIT_STR)

        compare_result = {
            'id': 0 if result_obj is None else result_obj.id,
            'application_id': case_id,
            'entity': entity,
            'scheme': consts.DOC_SCHEME_LIST[0] if scheme == consts.COMPARE_DOC_SCHEME_LIST[0] else consts.DOC_SCHEME_LIST[1],
            'whole_result': whole_result,
            'latest_compared_time': '' if result_obj is None else result_obj.update_time.strftime('%Y-%m-%d %H:%M'),
            'source': source,
            'version': version,
            'comments': comments,
            'result': [] if result_obj is None else json.loads(result_obj.result)
        }

        return response.ok(data=compare_result)

        # if len(result_str_list) == 0:
        #     compare_result_list = []
        # else:
        #     compare_result_list = json.loads(result_str_list[0])
        #
        # if len(compare_result_list) == 0:
        #     body_html = "<h1>没有比对结果</h1>"
        # else:
        #     head_content = ''.join(['<th>{0}</th>'.format(head_name) for head_name in consts.HEAD_LIST])
        #     head_html = '<tr>{0}</tr>'.format(head_content)
        #     row_html_list = []
        #     for row_dict in compare_result_list:
        #         row_list = [row_dict.get(head, '') for head in consts.HEAD_LIST]
        #         row_content = ''.join(['<td>{0}</td>'.format(row_str) for row_str in row_list])
        #         row_html = '<tr>{0}</tr>'.format(row_content)
        #         row_html_list.append(row_html)
        #     content_html = ''.join(row_html_list)
        #     body_html = '<table border="1">{0}{1}</table>'.format(head_html, content_html)
        #
        # html = """
        # <!DOCTYPE html>
        # <html>
        # <head>
        # <meta charset="utf-8">
        # <title>比对结果</title>
        # </head>
        # <body>
        # {0}
        # </body>
        # </html>
        # """.format(body_html)
        # return HttpResponse(html)

    # 比对结果更新
    @use_args(result_update_args, location='data')
    def post(self, request, args):
        result_id = args.get('id')
        case_id = args.get('application_id')
        update_time = args.get('latest_compared_time')
        scheme = args.get('scheme')
        entity = args.get('entity')
        whole_result = args.get('whole_result')

        if entity == consts.HIL_PREFIX:
            result_table = HILCACompareResult if scheme == consts.DOC_SCHEME_LIST[0] else HILSECompareResult
            record_table = HILCACompareResultRecord if scheme == consts.DOC_SCHEME_LIST[0] else HILSECompareResultRecord
        else:
            result_table = AFCCACompareResult if scheme == consts.DOC_SCHEME_LIST[0] else AFCSECompareResult
            record_table = AFCCACompareResultRecord if scheme == consts.DOC_SCHEME_LIST[0] else AFCSECompareResultRecord

        if result_id is not None:
            result_obj = result_table.objects.filter(id=result_id).first()
        else:
            result_obj = result_table.objects.filter(application_id=case_id).first()

        if result_obj is None:
            return response.ok()

        result_str = json.dumps(args.get('result', []))
        is_finish = whole_result == consts.RESULT_Y
        compare_count = 0
        failed_count = 0
        reason_dict = {}
        for result in args.get('result', []):
            compare_count += 1
            if result.get(consts.HEAD_LIST[6]) == consts.RESULT_N:
                failed_count += 1
                error_type = result.get(consts.HEAD_LIST[-1], '')
                if error_type in reason_dict:
                    reason_dict[error_type] = reason_dict[error_type] + 1
                else:
                    reason_dict[error_type] = 1

        record_table.objects.create(
            application_id=case_id,
            is_finish=is_finish,
            compare_count=compare_count,
            failed_count=failed_count,
            reason1_count=reason_dict.get(0, 0),
            reason2_count=reason_dict.get(1, 0),
            reason3_count=reason_dict.get(2, 0),
            reason4_count=reason_dict.get(3, 0),
            reason5_count=reason_dict.get(4, 0),
            reason6_count=reason_dict.get(5, 0),
            reason7_count=reason_dict.get(6, 0),
            reason8_count=reason_dict.get(7, 0),
            reason9_count=reason_dict.get(8, 0),
            reason10_count=reason_dict.get(9, 0),
            result=result_str,
            comments=args.get('comments', ''),
        )

        if update_time == result_obj.update_time.strftime('%Y-%m-%d %H:%M'):
            result_obj.result = result_str
            result_obj.comments = args.get('comments', '')
            # result_obj.update_time = result_obj.update_time
            result_obj.save()
            return response.ok()
        else:
            whole_result = consts.RESULT_Y if result_obj.is_finish else consts.RESULT_N

            if isinstance(result_obj.comments, str):
                comments = result_obj.comments
            else:
                comments = ''

            if isinstance(result_obj.version, str):
                source, version = result_obj.version.split(consts.SPLIT_STR)
            else:
                source = ''
                version = ''

            compare_result = {
                'id': result_obj.id,
                'application_id': case_id,
                'entity': entity,
                'scheme': consts.DOC_SCHEME_LIST[0] if scheme == consts.COMPARE_DOC_SCHEME_LIST[0] else
                consts.DOC_SCHEME_LIST[1],
                'whole_result': whole_result,
                'latest_compared_time': result_obj.update_time.strftime('%Y-%m-%d %H:%M'),
                'source': source,
                'version': version,
                'comments': comments,
                'result': json.loads(result_obj.result)
            }

            return response.need_update(data=compare_result)


class ResourcesView(GenericView):
    permission_classes = []
    authentication_classes = []

    def get(self, request):
        error_type_map = ErrorType.get_mappings()
        auto_result_map = AutoResult.get_mappings()
        whole_result_map = WholeResult.get_mappings()
        rpa_result_map = RPAResult.get_mappings()
        error_type = [{'label': v, 'value': k} for k, v in error_type_map.items()]
        auto_result = [{'label': v, 'value': k} for k, v in auto_result_map.items()]
        whole_result = [{'label': v, 'value': k} for k, v in whole_result_map.items()]
        rpa_result = [{'label': v, 'value': k} for k, v in rpa_result_map.items()]
        resources = {
            'error_type': error_type,
            'auto_result': auto_result,
            'whole_result': whole_result,
            'rpa_result': rpa_result
        }
        return response.ok(data=resources)


class SECMSView(GenericView):
    permission_classes = [IsAuthenticated]
    authentication_classes = [OAuth2AuthenticationWithUser]

    # CMS上传比对信息接口 SE
    # @use_args(se_cms_args, location='data')
    def post(self, request):  # interface_report cms to ocr
        start_time = time.time()

        args = request.data
        cms_info = args.get('content', {})
        business_type = consts.AFC_PREFIX if cms_info.get('financeCompany', '').startswith('宝马') else consts.HIL_PREFIX
        src_application_id = cms_info.get('settlemnetVerification', {}).get('applicationNo', '')
        application_id = src_application_id[:src_application_id.rfind('-')]

        # auto flag
        is_auto = cms_info.get('AutoSettlement', False)

        auto_class = HILAutoSettlement if business_type in consts.HIL_SET else AFCAutoSettlement
        auto_obj = auto_class.objects.filter(application_id=application_id).first()
        if is_auto:
            # 加入优先级队列
            PriorityApplication.objects.update_or_create(
                application_id=application_id, defaults={'on_off': True})
            # 加入auto表
            if auto_obj is None:
                auto_class.objects.create(
                    application_id=application_id,
                )
            elif auto_obj.on_off is False:
                auto_obj.on_off = True
                auto_obj.save()
        else:
            if auto_obj is not None and auto_obj.on_off is True:
                auto_obj.on_off = False
                auto_obj.save()

        # 比对信息存储
        content_str = json.dumps(cms_info)

        comparison_class = HILSECMSInfo if business_type in consts.HIL_SET else AFCSECMSInfo
        comparison_class.objects.create(
            application_id=application_id,
            content=content_str,
        )

        # 触发比对
        compare.apply_async((application_id, business_type, None, None, False, True),
                            queue='queue_compare')

        try:
            end_time = time.time()
            duration_second = int(end_time - start_time)
            InterfaceReport.objects.create(
                source=SystemName.CMS.name,
                target=SystemName.OCR.name,
                body=json.dumps(args),
                response=None,
                status=True,
                # retry_times=None,
                duration=duration_second,
            )
        except Exception as e:
            self.exception_log.exception('[cms view] [db save failed] [error={0}]'.format(traceback.format_exc()))

        return response.ok()

    post.openapi_doc = '''
        tags: [info]
        summary: CMS上传比对信息
        consumes: [application/json]
        produces: [application/json]
        parameters:
        - in: body
          name: body
          required: true
          schema:
            $ref: "#/definitions/CMS"

        responses:
          200:
            description: ok
            schema:
              $ref: '#/definitions/ApiResponse'
        '''


class SEContractView(GenericView):
    permission_classes = [IsAuthenticated]
    authentication_classes = [OAuth2AuthenticationWithUser]

    # pos上传e-contract信息接口 SE
    @use_args(se_contract_args, location='data')
    def post(self, request, args):
        # contract_info = args.get('content', {})
        # application_id = contract_info.get('applicationId', '')
        # entity = contract_info.get('applicationEntity', '')
        # table_class = HILContract if entity == consts.HIL_PREFIX else AFCContract
        # table_class.objects.create(application_id=application_id)
        # forwarding_station.apply_async((application_id, entity), queue='queue_compare', countdown=conf.DELAY_SECONDS)
        self.running_log.info('[e-contract pos in] [args={0}]'.format(args))
        return response.ok()


class AutoSettlementView(GenericView):
    # permission_classes = []
    # authentication_classes = []
    # permission_classes = [IsAuthenticated]
    # authentication_classes = [OAuth2AuthenticationWithUser]

    # 获取auto settlement列表
    @use_args(auto_list_args, location='querystring')
    def get(self, request, args):
        page = args.get('page', consts.PAGE_DEFAULT)
        page_size = args.get('page_size', consts.PAGE_SIZE_DEFAULT)
        business_type = args.get('business_type')
        application_id = args.get('application_id')

        get_case_from_ocr_time_start = args.get('get_case_from_ocr_time_start')
        get_case_from_ocr_time_end = args.get('get_case_from_ocr_time_end')
        activated_time_start = args.get('activated_time_start')
        activated_time_end = args.get('activated_time_end')
        comparison_time_start = args.get('comparison_time_start')
        comparison_time_end = args.get('comparison_time_end')

        auto_result = args.get('auto_result', '')
        whole_result = args.get('whole_result', '')
        rpa_result = args.get('rpa_result', '')

        if isinstance(auto_result, int):
            auto_result = consts.RESULT_MAP.get(auto_result)
        if isinstance(whole_result, int):
            whole_result = consts.RESULT_MAP.get(whole_result)
        if isinstance(rpa_result, int):
            rpa_result = consts.RPA_RESULT_MAP.get(rpa_result)

        application_id_query = Q(application_id__contains=application_id) if application_id is not None else Q()
        auto_result_query = Q(ocr_auto_result_pass=auto_result) if not isinstance(auto_result, str) else Q()
        whole_result_query = Q(ocr_whole_result_pass=whole_result) if not isinstance(whole_result, str) else Q()
        rpa_result_query = Q(rpa_result=rpa_result) if not isinstance(rpa_result, str) else Q()
        time1_query = Q(rpa_get_case_from_ocr_time__gte=get_case_from_ocr_time_start,
                        rpa_get_case_from_ocr_time__lt=get_case_from_ocr_time_end + datetime.timedelta(days=1))\
            if get_case_from_ocr_time_start is not None and get_case_from_ocr_time_end is not None else Q()
        time2_query = Q(rpa_activated_time__gte=activated_time_start,
                        rpa_activated_time__lt=activated_time_end + datetime.timedelta(days=1)) \
            if activated_time_start is not None and activated_time_end is not None else Q()
        time3_query = Q(ocr_latest_comparison_time__gte=comparison_time_start,
                        ocr_latest_comparison_time__lt=comparison_time_end + datetime.timedelta(days=1)) \
            if comparison_time_start is not None and comparison_time_end is not None else Q()

        query = application_id_query & auto_result_query & whole_result_query & rpa_result_query \
                & time1_query & time2_query & time3_query

        auto_class = HILAutoSettlement if business_type in consts.HIL_SET else AFCAutoSettlement

        total = auto_class.objects.filter(query).count()
        start_index = page_size * (page - 1)
        end_index = page_size * page
        if start_index >= total > 0:
            raise self.invalid_params('页数不存在')

        val_tuple = ('application_id', 'ocr_latest_comparison_time', 'ocr_auto_result_pass', 'ocr_whole_result_pass',
                     'rpa_result', 'rpa_activated_time', 'rpa_get_case_from_ocr_time')
        auto_queryset = auto_class.objects.filter(query).values(*val_tuple).order_by(
            '-ocr_latest_comparison_time')[start_index: end_index]

        auto_link_base = '{0}/showList/showList?entity={1}&scheme={2}&case_id={3}&auto=1'
        for auto_dict in auto_queryset:
            auto_dict['ocr_auto_result_pass'] = consts.RE_RESULT_MAP.get(auto_dict.get('ocr_auto_result_pass'))
            auto_dict['ocr_whole_result_pass'] = consts.RE_RESULT_MAP.get(auto_dict.get('ocr_whole_result_pass'))
            auto_dict['rpa_result'] = consts.RE_RPA_RESULT_MAP.get(auto_dict.get('rpa_result'))
            auto_dict['target_url'] = auto_link_base.format(
                conf.BASE_URL,
                business_type,
                consts.COMPARE_DOC_SCHEME_LIST[1],
                auto_dict['application_id'])

        # total = len(doc_list)
        pagination = {'current': page, 'total': total, 'page_size': page_size}
        res = {
            'pagination': pagination,
            'auto_list': list(auto_queryset)
        }
        self.running_log.info('[get auto list] [args={0}] [res={1}]'.format(args, res))
        return response.ok(data=res)


class AutoSettlementExcelView(GenericView):
    # permission_classes = []
    # authentication_classes = []
    # permission_classes = [IsAuthenticated]
    # authentication_classes = [OAuth2AuthenticationWithUser]

    # 获取auto settlement excel
    @use_args(auto_list_args, location='querystring')
    def get(self, request, args):
        business_type = args.get('business_type')
        application_id = args.get('application_id')

        get_case_from_ocr_time_start = args.get('get_case_from_ocr_time_start')
        get_case_from_ocr_time_end = args.get('get_case_from_ocr_time_end')
        activated_time_start = args.get('activated_time_start')
        activated_time_end = args.get('activated_time_end')
        comparison_time_start = args.get('comparison_time_start')
        comparison_time_end = args.get('comparison_time_end')

        auto_result = args.get('auto_result', '')
        whole_result = args.get('whole_result', '')
        rpa_result = args.get('rpa_result', '')

        if isinstance(auto_result, int):
            auto_result = consts.RESULT_MAP.get(auto_result)
        if isinstance(whole_result, int):
            whole_result = consts.RESULT_MAP.get(whole_result)
        if isinstance(rpa_result, int):
            rpa_result = consts.RPA_RESULT_MAP.get(rpa_result)

        application_id_query = Q(application_id__contains=application_id) if application_id is not None else Q()
        auto_result_query = Q(ocr_auto_result_pass=auto_result) if not isinstance(auto_result, str) else Q()
        whole_result_query = Q(ocr_whole_result_pass=whole_result) if not isinstance(whole_result, str) else Q()
        rpa_result_query = Q(rpa_result=rpa_result) if not isinstance(rpa_result, str) else Q()
        time1_query = Q(rpa_get_case_from_ocr_time__gte=get_case_from_ocr_time_start,
                        rpa_get_case_from_ocr_time__lt=get_case_from_ocr_time_end + datetime.timedelta(days=1)) \
            if get_case_from_ocr_time_start is not None and get_case_from_ocr_time_end is not None else Q()
        time2_query = Q(rpa_activated_time__gte=activated_time_start,
                        rpa_activated_time__lt=activated_time_end + datetime.timedelta(days=1)) \
            if activated_time_start is not None and activated_time_end is not None else Q()
        time3_query = Q(ocr_latest_comparison_time__gte=comparison_time_start,
                        ocr_latest_comparison_time__lt=comparison_time_end + datetime.timedelta(days=1)) \
            if comparison_time_start is not None and comparison_time_end is not None else Q()

        query = application_id_query & auto_result_query & whole_result_query & rpa_result_query \
                & time1_query & time2_query & time3_query

        auto_class = HILAutoSettlement if business_type in consts.HIL_SET else AFCAutoSettlement

        auto_queryset = auto_class.objects.filter(query).values_list(*consts.AUTO_WB_FIELD[0]).order_by(
            '-ocr_latest_comparison_time')

        wb = Workbook()
        ws = wb.active
        ws.append(consts.AUTO_WB_FIELD[1])
        for row in auto_queryset:
            ws.append(row)
        io_content = io.BytesIO()  # 创建在内存中处理对象
        wb.save(io_content)
        wb.close()
        file_name = 'ocr_auto_records_{0}'.format(timezone.now().strftime('%Y-%m-%d_%H:%M:%S'))
        return response.excel_response(file_name, io_content)


class MPOSView(GenericView, MPOSHandler):
    permission_classes = [IsAuthenticated]
    authentication_classes = [OAuth2AuthenticationWithUser]

    # MPOS
    @use_args(mpos_args, location='data')
    def post(self, request, args):  # interface_report mpos to ocr
        start_time = time.time()

        classify = args.get('type')
        result_list = []
        image_count = 0
        all_success = True
        for img_base64 in args.get('file_base64_content', []):
            image_count += 1
            try:
                if classify in consts.LICENSE_CLASSIFY_SET_1:
                    result = self.ocr1_process(conf.MPOS_URL1, img_base64)
                else:
                    result = self.ocr2_process(conf.MPOS_URL2, classify, img_base64)

                result_list.extend(result)
            except Exception as e:
                all_success = False
                continue

        end_time = time.time()
        duration_second = int(end_time - start_time)

        try:
            MposReport.objects.create(
                doc_type=classify,
                image_count=image_count,
                status=all_success,
                duration=duration_second,
            )
        except Exception as e:
            self.exception_log.exception('[mpos view] [db save failed] [error={0}]'.format(traceback.format_exc()))

        try:
            InterfaceReport.objects.create(
                source=SystemName.MPOS.name,
                target=SystemName.OCR.name,
                body=None,
                response=json.dumps(result_list),
                status=True,
                # retry_times=None,
                duration=duration_second,
            )
        except Exception as e:
            self.exception_log.exception('[go view] [db save failed] [error={0}]'.format(traceback.format_exc()))

        return response.ok(data=result_list)


class GoView(GenericView):
    permission_classes = [IsAuthenticated]
    authentication_classes = [OAuth2AuthenticationWithUser]

    @use_args(go_args, location='files')
    def post(self, request, args):  # interface_report unknown to ocr
        result = None
        is_success = False
        start_time = time.time()
        try:
            files = [
                ('img', ('file', args.get('image'), 'application/octet-stream'))
            ]

            go_result = requests.post(url=conf.GO_OCR_URL, files=files)
        except Exception as e:
            pass
        else:
            if go_result.status_code == 200:
                is_success = True
                result = go_result.json().get('data', '')
        finally:
            end_time = time.time()
            duration_second = int(end_time - start_time)

            try:
                GenericOCRReport.objects.create(
                    status=is_success,
                    duration=duration_second,
                )
            except Exception as e:
                self.exception_log.exception('[go view] [db save failed] [error={0}]'.format(traceback.format_exc()))

            try:
                InterfaceReport.objects.create(
                    source=SystemName.UNKNOWN.name,
                    target=SystemName.OCR.name,
                    body=None,
                    response=json.dumps(result) if is_success else None,
                    status=is_success,
                    # retry_times=None,
                    duration=duration_second,
                )
            except Exception as e:
                self.exception_log.exception('[go view] [db save failed] [error={0}]'.format(traceback.format_exc()))

            if is_success:
                return response.ok(data=result)
            else:
                return response.error_msg(msg='识别错误')