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

from __future__ import division

import random
import re

from django.conf import settings
from django.db import transaction
from django_logit import logit
from django_response import response
from pywe_marketcode import tickettocode
from pywe_storage import RedisStorage

from account.models import UserInfo
from logs.models import MchInfoDecryptLogInfo, MchInfoEncryptLogInfo
from marketcode.models import MarketCodeInfo
from mch.models import ActivityInfo, BrandInfo, ModelInfo
from utils.algorithm.b64 import b64_decrypt, b64_encrypt
from utils.algorithm.caesar import caesar_decrypt, caesar_encrypt
from utils.algorithm.rsalg import rsa_decrypt, rsa_encrypt
from utils.error.errno_utils import MarketCodeStatusCode, UserStatusCode
from utils.redis.connect import r


WECHAT = settings.WECHAT

# CIPHER_ALGORITHM = ('CAESAR', 'B64', 'RSA')
CIPHER_ALGORITHM = ('CAESAR', )

CIPHER_PREFIX = {
    'CAESAR': '0',
    'B64': '1',
    'RSA': '2',
}


@logit(res=True)
def encrypt(request):
    plaintext = request.POST.get('plaintext', '')
    optor_id = request.POST.get('optor_id', '')
    marketcode = request.POST.get('marketcode', '')

    # brand_id#model_id#distributor_id#sn#time
    # AAAA#AAAAAA#AAAAA#AAAAAAAAAAAAAA#180224
    brand_pk, model_pk, distributor_pk, sn, time = plaintext.split('#')

    mieli, created_at = MchInfoEncryptLogInfo.objects.get_or_create(plaintext=plaintext)

    if marketcode:
        if created_at or not mieli.code:
            with transaction.atomic():
                marketcode = MarketCodeInfo.objects.select_for_update().filter(has_used=False).first()

                if not marketcode:
                    return response(MarketCodeStatusCode.MARKET_CODE_NOT_FOUND)

                marketcode.has_used = True
                marketcode.save()

                mieli.application_id = marketcode.application_id
                mieli.code = marketcode.code
                mieli.code_url = marketcode.code_url
                mieli.brand_pk = brand_pk
                mieli.model_pk = model_pk
                mieli.distributor_pk = distributor_pk
                mieli.sn = sn
                mieli.operator_id = optor_id
                mieli.save()

        return response(200, data={
            'ciphertext': mieli.code_url,
        })

    if created_at:
        alg = random.choice(CIPHER_ALGORITHM)

        if alg == 'CAESAR':
            ciphertext = caesar_encrypt(plaintext)
        elif alg == 'B64':
            ciphertext = b64_encrypt(plaintext)
        elif alg == 'RSA':
            ciphertext = rsa_encrypt(plaintext)
        else:
            ciphertext = plaintext

        mieli.alg = alg
        mieli.ciphertext = ciphertext
        mieli.brand_pk = brand_pk
        mieli.model_pk = model_pk
        mieli.distributor_pk = distributor_pk
        mieli.sn = sn
        mieli.operator_id = optor_id

    try:
        model = ModelInfo.objects.get(pk=model_pk)
    except ModelInfo.DoesNotExist:
        model = None

    if model:
        mieli.is_send_redpack = model.is_send_redpack
        mieli.redpack_amount = model.redpack_amount
        mieli.redpack_max_amount = model.redpack_max_amount
        mieli.is_clerk_send_redpack = model.is_clerk_send_redpack
        mieli.clerk_redpack_amount = model.clerk_redpack_amount
        mieli.clerk_redpack_max_amount = model.clerk_redpack_max_amount

    mieli.save()

    return response(200, data={
        'ciphertext': u'{prefix}+{cipherlen}+{ciphertext}'.format(
            prefix=CIPHER_PREFIX.get(mieli.alg, ''),
            cipherlen=len(mieli.ciphertext),
            ciphertext=mieli.ciphertext,
        ),
    })


@logit(res=True)
def decrypt(request):
    ciphertext = request.POST.get('ciphertext', '')

    prefix, cipherlen, ciphertext = ciphertext.split('+', 2)

    ciphertext = ciphertext[:int(cipherlen)]

    if prefix == CIPHER_PREFIX['CAESAR']:
        plaintext = caesar_decrypt(ciphertext)
    elif prefix == CIPHER_PREFIX['B64']:
        plaintext = b64_decrypt(ciphertext)
    elif prefix == CIPHER_PREFIX['RSA']:
        plaintext = rsa_decrypt(ciphertext)
    else:
        plaintext = ciphertext

    # brand_id#model_id#distributor_id#sn#time
    # AAAA#AAAAAA#AAAAA#AAAAAAAAAAAAAA#180224
    brand_pk, model_pk, distributor_pk, sn, time = plaintext.split('#')

    try:
        brand = BrandInfo.objects.get(pk=brand_pk)
    except BrandInfo.DoesNotExist:
        brand = None

    try:
        model = ModelInfo.objects.get(pk=model_pk)
    except ModelInfo.DoesNotExist:
        model = None

    mdli, created_at = MchInfoDecryptLogInfo.objects.get_or_create(ciphertext=ciphertext, defaults={
        'brand_pk': brand_pk,
        'model_pk': model_pk,
        'distributor_pk': distributor_pk,
        'sn': sn,
        'decrypt_count': 1,
    })

    if not created_at:
        mdli.decrypt_count += 1
        mdli.save()

    act = ActivityInfo.objects.filter(status=True).order_by('-pk').first()
    has_unexpired_activity = True if act and act.has_unexpired_activity(model.model_uni_name) else False

    coupon_info = {
        'coupon_expire_at': act.final_coupon_expire_at(created_at=None),
        'coupon_value': act.coupon_value,
    } if has_unexpired_activity else {
        'coupon_expire_at': '',
        'coupon_value': 0,
    }

    # 红包
    try:
        elog = MchInfoEncryptLogInfo.objects.get(plaintext=plaintext)
    except MchInfoEncryptLogInfo.DoesNotExist:
        elog = None
    except MchInfoEncryptLogInfo.MultipleObjectsReturned:
        elog = None

    return response(200, data={
        'plaintext': plaintext,
        'logo_url': brand.brand_logo_url if brand else '',
        'model_imgs': model.images if model else [],
        'model_imgs2': model.images2 if model else [],
        'goodsInfo': {
            'BrandID': brand_pk,
            'Brand': brand.brand_name if brand else '',
            'ModelID': model_pk,
            'model_id': model.model_id if model else '',
            'Model': (model.model_full_name or model.model_name) if model else '',
            'DistributorID': distributor_pk,
            'SerialNo': sn,
        },
        'has_unexpired_activity': has_unexpired_activity,
        'coupon_info': coupon_info,
        'redpack_info': elog.redpack_info if elog else {},
        'integral': model.integral,
    })


@logit(res=True)
def decrypt2(request):
    code_ticket = request.POST.get('code_ticket', '')
    code = request.POST.get('code', '')
    user_id = request.POST.get('user_id', '')

    if code_ticket and user_id:
        try:
            user = UserInfo.objects.get(user_id=user_id)
        except UserInfo.DoesNotExist:
            return response(UserStatusCode.USER_NOT_FOUND)

        wxcfg = WECHAT.get('JSAPI', {})

        appid = wxcfg.get('appID')
        secret = wxcfg.get('appsecret')

        code_info = tickettocode(code_ticket=code_ticket, openid=user.openid_miniapp, appid=appid, secret=secret,
                                 token=None, storage=RedisStorage(r))

        code = code_info.get('code', '')

        try:
            mieli = MchInfoEncryptLogInfo.objects.get(code=code)
        except MchInfoEncryptLogInfo.DoesNotExist:
            return response()
    else:
        code = re.sub(r'http://|https://', '', code)
        try:
            mieli = MchInfoEncryptLogInfo.objects.get(code_url=code)
        except MchInfoEncryptLogInfo.DoesNotExist:
            return response()

    plaintext = mieli.plaintext

    # brand_id#model_id#distributor_id#sn#time
    # AAAA#AAAAAA#AAAAA#AAAAAAAAAAAAAA#180224
    brand_pk, model_pk, distributor_pk, sn, time = plaintext.split('#')

    try:
        brand = BrandInfo.objects.get(pk=brand_pk)
    except BrandInfo.DoesNotExist:
        brand = None

    try:
        model = ModelInfo.objects.get(pk=model_pk)
    except ModelInfo.DoesNotExist:
        model = None

    mdli, created_at = MchInfoDecryptLogInfo.objects.get_or_create(
        application_id=mieli.application_id,
        code=mieli.code,
        code_url=mieli.code_url,
        defaults={
            'brand_pk': brand_pk,
            'model_pk': model_pk,
            'distributor_pk': distributor_pk,
            'sn': sn,
            'decrypt_count': 1,
        }
    )

    if not created_at:
        mdli.decrypt_count += 1
        mdli.save()

    act = ActivityInfo.objects.filter(status=True).order_by('-pk').first()
    has_unexpired_activity = True if act and act.has_unexpired_activity(model.model_uni_name) else False

    coupon_info = {
        'coupon_expire_at': act.final_coupon_expire_at(created_at=None),
        'coupon_value': act.coupon_value,
    } if has_unexpired_activity else {
        'coupon_expire_at': '',
        'coupon_value': 0,
    }

    return response(200, data={
        'plaintext': plaintext,
        'logo_url': brand.brand_logo_url if brand else '',
        'model_imgs': model.images if model else [],
        'goodsInfo': {
            'BrandID': brand_pk,
            'Brand': brand.brand_name if brand else '',
            'ModelID': model_pk,
            'Model': (model.model_full_name or model.model_name) if model else '',
            'DistributorID': distributor_pk,
            'SerialNo': sn,
        },
        'has_unexpired_activity': has_unexpired_activity,
        'coupon_info': coupon_info,
    })
