import re
import sys
import json
import time
import pymysql

from tw.config import conn, table
from datetime import datetime
from random import randint
from collections import namedtuple
from openpyxl import Workbook
from openpyxl.styles import PatternFill, colors


'''
twong 辅助工具，扫描商品表，及其关联表

自动更新不合理数据

对于价格方面的数据，则导出为 excel 表

运行需自行配置参数，并打开 commit 语句
'''

SHOW_MAX_DAYS = 800  # 超过天数自动下架


NOW = int(time.time())      # 当前时间戳

INVALID_CHAR=r'[\s，。 ,.{}()\[\]<>]+'
PTN_INVALID = r'^{}|{}$'.format(INVALID_CHAR, INVALID_CHAR)

# config excel
excel = Workbook()
sheet = excel.active
sheet.title = '问题商品'
sheet.freeze_panes = 'A2' # freeze above or left

# init sheet header
sheet['A1'] = 'ID'
sheet['B1'] = '商品'
sheet['C1'] = 'SKU'
sheet['D1'] = '成本'
sheet['E1'] = '价格'
sheet['F1'] = '原价'
sheet['G1'] = '利润率'
sheet['H1'] = '其他问题'

for cols in sheet.iter_cols(min_col=1,max_col=8,min_row=1,max_row=1):
    for cell in cols:
        cell.fill = PatternFill('solid', fgColor='C5C1AA')
sheet['A1'].fill = PatternFill('solid', fgColor='C5C1AA')


def updateResult(productId, result, skuName, stock=0, price=0, otPrice=0, brokerage=0, brokerage2=0):
    '''
    更新 result 表中 result 字段中的值
    '''
    if not result:
        print('product={} result={}'.format(productId, result))
        return
    # print('updateResult:', productId, skuName)
    values = result.get('value')
    if not values:
        print('ERROR: no value field')
        return
    if isinstance(values, list):
        pass
    elif isinstance(values, dict):
        values = values.values()
    else:
        print('ERROR: bad value type')

    for value in values:
        detail = value.get('detail')
        if not detail:
            print('ERROR: no detail')
            return
        values = detail.values()
        values = [str(v) for v in values]
        sku = ','.join(sorted(values))
        if sku == skuName:
            value['stock'] = stock if stock else value.get('stock')
            value['ot_price'] = otPrice if otPrice else value['ot_price']
            value['price'] = price if price else value['price']
            value['brokerage'] = 0 if brokerage else 0
            value['brokerage_two'] = 0 if brokerage2 else 0
    return result
            

def fixResultInvalidChar(productId, result):
    '''
    检查 result 中规格中的各种非法字符
    '''
    if not result:
        return
    attrs = result.get('attr', [])
    if attrs:
        for attr in attrs:
            value = attr.get('value', '')
            attr['value'] = re.sub(PTN_INVALID, '', value)
            
            detail = attr.get('detail', [])
            for i, d in enumerate(detail):
                detail[i] = re.sub(PTN_INVALID, '', str(d))

    values = result.get('value', [])
    if isinstance(values, list):
        pass
    elif isinstance(values, dict):
        values = values.values()
    else:
        print('unknown value type')
        return
    for value in values:
        for i in range(len(attrs)):
            key = 'value{}'.format(i+1)
            valuex = value.get(key)
            value[key] = re.sub(PTN_INVALID, '', valuex)
            
        detail = value.get('detail', {})
        removing_keys = []
        adding_keys = {}
        for k, v in detail.items():
            if re.search(PTN_INVALID, k):
                removing_keys.append(k)
                newk = re.sub(PTN_INVALID, '', k)
                adding_keys[newk] = re.sub(PTN_INVALID, '', v)
            detail[k] = re.sub(PTN_INVALID, '', v)
        
        for k in removing_keys:
            del detail[k]
        for k, v in adding_keys.items():
            detail[k] = v

with conn:
    with conn.cursor() as cursor:
        sql = '''SELECT id, store_name, image, slider_image, spec_type, price, cost, stock, ficti, add_time
            FROM {} 
            WHERE is_show=1 AND is_del=0 '''.format(table('store_product'))
        cursor.execute(sql)
        products = cursor.fetchall()
        errors = {}
        # 遍历所有商品
        for pd in products:
            productId = pd[0]
            productName = pd[1]
            specType = pd[4]
            image =pd[2]
            images = json.loads(pd[3])
            ficti = pd[8]
            addTime = pd[9]
            chFicti = False             # 是否需要更新虚拟销量
            chProductStock = False      # 是否需要更新库存
            errors[productId] = {
                'name': productName,
            }

            sq2 = '''SELECT result 
                FROM {}
                WHERE product_id={}'''.format(table('store_product_attr_result'), productId)
            cursor.execute(sq2)
            result = cursor.fetchone()
            if not result or len(result) <= 0:
                # print('{}-{} 在 result 表中无数据.'.format(productId, productName))
                errors[productId]['others'] = errors[productId].get('others', '') + 'attr_result 表中无数据;'
                result = {}
            else:
                result = json.loads(result[0])
                fixResultInvalidChar(productId, result)

            sq3 = '''SELECT suk, stock, sales, price, cost, ot_price, brokerage, brokerage_two, `unique`
                FROM {}
                WHERE product_id={}'''.format(table('store_product_attr_value'), productId)
            cursor.execute(sq3)
            values = cursor.fetchall()

            # 检查商品图片是否完整
            if not image or not images:
                errors[productId]['others'] = errors[productId].get('others', '') + ' 没有设置产品图;'
            
            # 虚拟销量必须为 0-20
            if ficti < 0 or ficti > 20:
                chFicti = True

            # 上架超过一定时间自动下架，并将 add_time 改为当前日期
            days = int( (NOW - addTime) / 86400 )
            if days > SHOW_MAX_DAYS:
                sql = '''UPDATE {} SET is_show=0, add_time={} WHERE id={}'''.format(table('store_product'), NOW, productId)
                with conn.cursor() as cs:
                    cs.execute(sql)
                    print('{}-{} 过期{}天 已启动下架'.format(productId, productName, days))

            # 检查 price, cost, stock 必须为正值
            chResult = False                # 是否需要更新 attr_result 表
            errors[productId]['sku'] = {}   # 商品 sku 错误记录
            totalStock = 0
            eachStock = 0                   # 每个 sku 的库存，为了让sku 一致
            for sku in values:
                chStock, chOtPrice, chBrokerage, chName = False, False, False, False
                skuName = sku[0]
                skuNameList = skuName.split(',')
                for i, p in enumerate(skuNameList):
                    if re.search(PTN_INVALID, str(p)):
                        chName = True
                        skuNameList[i] = re.sub(PTN_INVALID, '', p)
                # print(skuNameList)
                newNameList = list(set(skuNameList))
                if chName or len(newNameList) != len(skuNameList):
                    skuName = ','.join(sorted(newNameList))

                stock = sku[1]
                sales = sku[2]
                price = sku[3]
                cost = sku[4]
                otPrice = sku[5]
                brokerage = sku[6]
                brokerage2 = sku[7]
                unique = sku[8]
                errors[productId]['sku'][skuName] = {}

                # 库存为 5-200
                if stock < 5 or stock > 200: # stock
                    chStock = True
                    chProductStock = True
                else:
                    totalStock += stock

                # 利润率 >= 30%
                if price <= 0: # price
                    errors[productId]['sku'][skuName]['price'] = float(price)
                if cost <= 0: # cost
                    errors[productId]['sku'][skuName]['cost'] = float(cost)
                if price < cost:
                    errors[productId]['sku'][skuName]['price'] = float(price)
                    errors[productId]['sku'][skuName]['cost'] = float(cost)
                    errors[productId]['sku'][skuName]['others'] = errors[productId]['sku'][skuName].get('others', '') + '价格低于成本;'
                if price > 0:
                    profitRate = round(float(1 - cost/price), 2)
                    if profitRate < 0.3:
                        errors[productId]['sku'][skuName]['price'] = float(price)
                        errors[productId]['sku'][skuName]['cost'] = float(cost)
                        errors[productId]['sku'][skuName]['profit'] = profitRate
                        errors[productId]['sku'][skuName]['others'] = errors[productId]['sku'][skuName].get('others', '') + ' 利润率不足 30%;'
                if otPrice < price:
                    chOtPrice = True
                # 商品自定义返利必须为0 
                if brokerage !=0 or brokerage2 != 0:
                    chBrokerage = True

                # 更新 result 结构
                if not eachStock or eachStock < sales:
                    eachStock = randint(50, 150)+sales if chStock else 0
                newOtPrice = int(float(price) * 1.4) if chOtPrice else 0
                newBrokerage = 1 if chBrokerage else 0
                newBrokerage2 = 1 if chBrokerage else 0
                updateResult(productId, result, skuName, stock=eachStock, price=0, otPrice=newOtPrice, brokerage=newBrokerage, brokerage2=newBrokerage2)
                # print(result)
                # 更新 SQL (attr_value 表)
                parts = []
                if chName:
                    parts.append("suk='{}'".format(skuName))
                if chStock:
                    totalStock += eachStock
                    parts.append('stock={}'.format(eachStock - sales))
                if chOtPrice:
                    parts.append('ot_price={}'.format(newOtPrice))
                if parts:
                    sparts = ','.join(parts)
                    sql = ''' UPDATE {} 
                        SET {}, brokerage=0, brokerage_two=0
                        WHERE `unique`='{}' '''.format(table('store_product_attr_value'), sparts, unique)
                    with conn.cursor() as cs:
                        cs.execute(sql)
                        print('{}-{}-{} 自动修改库存名称等: sql={}'.format(productId, productName, skuName, sql))
                
                chResult = (chResult or chStock or chOtPrice or chBrokerage)

            # 更新 attr_result 表
            if chResult:
                sql = '''UPDATE {}
                    SET result='{}'
                    WHERE product_id={}'''.format(table('store_product_attr_result'), json.dumps(result, ensure_ascii=False), productId)

                with conn.cursor() as cs:
                    cs.execute(sql)
                    print('更新 result. reason: {}, : sql={}'.format(sparts, sql))
            # update products
            parts = []
            if chFicti:
                newVal = randint(0, 20)
                parts.append('ficti={}'.format(newVal))
            if chProductStock:
                parts.append('stock={}'.format(totalStock))
            
            if parts:
                sparts = ','.join(parts)
                sql = '''UPDATE {} 
                SET {}
                WHERE id={}'''.format(table('store_product'), sparts, productId)
                with conn.cursor() as cs:
                    cs.execute(sql)
                    print('{}-{} 更改虚拟销量和库存 {}'.format(productId, productName, sparts))
        conn.commit()

counter = 1 # skip header line
cell = lambda ch,c: '{}{}'.format(ch, c)

for pId, v in errors.items():
    writen = False
    if len(v) > 2:
        counter += 1
        sheet[cell('A', counter)] = pId
        sheet[cell('B', counter)] = v['name']
        sheet[cell('H', counter)] = v.get('others', '')
        writen = True

    for k, s in v['sku'].items():
        if len(s) > 0:
            counter += 1
            if not writen:
                sheet[cell('A', counter)] = pId
                sheet[cell('B', counter)] = v['name']
                sheet[cell('H', counter)] = v.get('other', '')
                writen = True
            sheet[cell('C', counter)] = k
            sheet[cell('D', counter)] = s.get('cost', '')
            sheet[cell('E', counter)] = s.get('price', '')
            sheet[cell('F', counter)] = s.get('ot_price', '')
            sheet[cell('G', counter)] = s.get('profit', '')
            sheet[cell('H', counter)] = s.get('others', '')
            
# sheet['A1'] = 'ID'
# sheet['B1'] = '商品'
# sheet['C1'] = 'SKU'
# sheet['D1'] = '成本'
# sheet['E1'] = '价格'
# sheet['F1'] = '原价'
# sheet['G1'] = '利润率'
# sheet['H1'] = '其他问题'
excel.save(filename=datetime.now().strftime('%Y-%m-%d.xls'))
sys.exit()