#!/usr/bin/python

import datetime
import logging
import os

import requests
from bs4 import BeautifulSoup

from common import exception
from model import order_model
from model import product_model
from util.dict_util import dict2obj
from util.json_util import obj2json, json2dict

_MERCHANT_BASE_URL = "https://merchant.vova.com.hk"
_PRODUCT_BASE_URL = "https://merchant.vova.com.hk/api/v1"
_ORDER_BASE_URL = "https://merchant-api.vova.com.hk/v1"
_SUCCESS_CODE = 20000
_PRODUCT_ALREADY_EXIST_ERROR_CODE = 40015
_SKU_IMAGE_MAX_NUM = 20
_EXECUTE_SUCCESS = 'success'
_PRODUCT_DETAIL_PAGE_PREFIX_URL = "https://www.vova.com/-m"

USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36"

'''
VOVA商家
'''

_DEFAULT_TIMEOUT = 10


def build_product_api_url(uri):
    return _PRODUCT_BASE_URL + uri


def build_order_api_url(uri):
    return _ORDER_BASE_URL + uri


def check_response(resp):
    if not resp.ok:
        raise exception.BizException("Http error, status={}, msg={}".format(resp.status_code, resp.text))


def json_header():
    headers = {"Content-Type": "application/json"}
    return headers


def get_product_image(token, product_id):
    uri = "/product/getProductImgInfo"

    img_list_obj = []
    succeed = False
    retry_times = 5
    while not succeed and retry_times > 0:
        response_text = ''
        try:
            response = requests.get(build_product_api_url(uri) + "?token=" + token + "&product_id=" + product_id,
                                    timeout=_DEFAULT_TIMEOUT)
            check_response(response)
            response_text = response.text
        except Exception as err:
            logging.error("Error: {}, remain retry times {}".format(err, retry_times))
            retry_times = retry_times - 1
            continue

        succeed = True
        response_dict = json2dict(response_text)
        product_img_list = response_dict["product_img_info"]
        for img in product_img_list:
            img_id = img["img_id"]
            img_url = img["img_url"]
            goods_sku_list = img["goods_sku_list"]
            is_main_img = True
            is_main_img_str = img['main_img']
            if 'N' == is_main_img_str:
                is_main_img = False
            is_sku_image = True
            if not goods_sku_list:
                is_sku_image = False
            img_list_obj.append(product_model.ProductImage(img_id, img_url, goods_sku_list, is_main_img, is_sku_image))

    return img_list_obj


def get_product_list(token, start_time, end_time):
    page = 1
    total_page = 1
    result_list = []
    while page <= total_page:
        total_page, product_list = get_product_list_by_page(token, start_time, end_time, page)
        if total_page <= 0 or not product_list:
            break
        page = page + 1
        result_list.extend(product_list)
    return result_list


def get_product_list_by_page(token, start_time, end_time, page=1, per_page=100):
    uri = "/product/productList"

    params = {
        'token': token,
        'conditions': {
            'add_time': {
                'start': start_time,
                'end': end_time
            },
            'page_arr': {
                'perPage': per_page,
                'page': page
            }
        }
    }

    response_text = ""
    try:
        response = requests.post(build_product_api_url(uri), headers=json_header(), data=obj2json(params),
                                 timeout=_DEFAULT_TIMEOUT)
        check_response(response)
        response_text = response.text
    except Exception as err:
        logging.error("Error: {}".format(err))
        return 0, None

    response_dict = json2dict(response_text)
    page_arr = response_dict["page_arr"]
    total_page = int(page_arr["totalPage"])
    total_amount = int(page_arr["totalAmount"])

    logging.info("Total page {} and total num {} between {}-{}, current page {}"
                 .format(total_page, total_amount, start_time, end_time, page))

    if page > total_page:
        # logging.error("Page {} greater than total {}".format(page, total_page))
        return 0, None

    product_list = response_dict["product_list"]
    if not product_list:
        return 0, None

    logging.info("Found {} product".format(len(product_list)))

    product_list_obj = []
    count = 0
    for product in product_list:
        complete_ratio = round(count / len(product_list) * 100, 2)
        count = count + 1

        is_on_sale = bool(product["is_on_sale"])
        if not is_on_sale:
            continue

        product_id = product["product_id"]
        cat_id = product["cat_id"]
        goods_name = product["goods_name"]
        goods_description = product["goods_description"]
        parent_sku = product["parent_sku"]
        main_image = product["main_image"]

        product_image_list = get_product_image(token, product_id)
        if not product_image_list:
            continue

        # update main image
        main_image_list = [img.url for img in product_image_list if img.main_image]
        if main_image_list:
            main_image = main_image_list[0]

        sku_list = product["sku_list"]
        if not sku_list:
            continue
        sku_obj_list = []
        for sku in sku_list:
            goods_sku = sku["goods_sku"]
            shop_price = sku["shop_price"]
            storage = sku["storage"]
            shipping_fee = sku["shipping_fee"]
            style_info = json2dict(sku["style_info"])
            style_color = ""
            if "color" in style_info:
                style_color = style_info["color"]
            style_size = ""
            if "size" in style_info:
                style_size = style_info["size"]

            sku_image_list = []
            sku_image_list.extend([s.url for s in product_image_list if goods_sku in s.goods_sku_list])
            extra_image_list = []
            extra_image_list.extend([s.url for s in product_image_list if not s.goods_sku_list])
            # 图片超过最大数量时，去掉部分图片
            if len(extra_image_list) > _SKU_IMAGE_MAX_NUM:
                logging.warning("Image number exceed, num={}, max={}"
                                .format(len(extra_image_list), _SKU_IMAGE_MAX_NUM))
                extra_image_list = extra_image_list[0:_SKU_IMAGE_MAX_NUM]
            sku_obj_list.append(product_model.Sku(goods_sku, shop_price, storage, shipping_fee, style_color,
                                                  style_size, sku_image_list[0], extra_image_list))

        product_list_obj.append(product_model.Product(product_id, cat_id, main_image, goods_name,
                                                      goods_description, parent_sku, sku_obj_list, product_image_list))

        logging.info("[{}%] Process product {} - {}".format(complete_ratio, product_id, goods_name))

    return total_page, product_list_obj


def add_product_sku(token, parent_sku, goods_sku, storage, market_price, shop_price, shipping_fee, sku_image):
    uri = "/product/addProductSku"

    params = {
        "token": token,
        "items": [
            {
                "parent_sku": parent_sku,
                "goods_sku": goods_sku,
                "storage": storage,
                "market_price": market_price,
                "shop_price": shop_price,
                "shipping_fee": shipping_fee,
                "sku_image": sku_image,
            }
        ]
    }

    response = requests.post(build_product_api_url(uri), headers=json_header(), data=obj2json(params))
    check_response(response)
    response_dict = json2dict(response.text)

    if response_dict["data"]["code"] != _SUCCESS_CODE:
        raise exception.BizException("Failed to add product sku")


def parse_warning_list(warn_list):
    warns_result = []
    for warn in warn_list:
        warning_info_list = warn["warning_info"]
        warns_result.extend([warn["message"] for warn in warning_info_list])
    return warns_result


def upload_product(token, product_list_dto):
    uri = "/product/uploadGoods"

    params = {
        "token": token,
        "items": product_list_dto,
        "ignore_warning": 0,
        "is_cn": 0,
    }

    data = obj2json(params).encode("utf-8").decode("latin1")
    response = requests.post(build_product_api_url(uri), headers=json_header(), data=data)
    check_response(response)

    response_dict = json2dict(response.text)
    response_data = response_dict["data"]
    if response_data["code"] != _SUCCESS_CODE:
        if response_data["code"] == _PRODUCT_ALREADY_EXIST_ERROR_CODE:
            logging.warning("Product already exist, ignore")
        else:
            logging.info("Upload failed, request = {}".format(obj2json(product_list_dto)))
            logging.info("Upload failed, {}".format(response.text))
            msg = response_data["message"]
            error_list_msg = ""
            warn_list_msg = ""
            if "errors_list" in response_data:
                error_list_msg += obj2json(response_data["errors_list"])
            if "warnings_list" in response_data:
                warn_list_msg += obj2json(response_data["warnings_list"])
            raise exception.BizException("Failed to upload product, msg is {}, error_msg is {}, warn_msg is {}"
                                         .format(msg, error_list_msg, warn_list_msg))

    return response_data["upload_batch_id"]


def get_upload_status_by_batch_id(token, upload_batch_id):
    uri = "/product/getUploadGoodsStatus"

    params = {
        "token": token,
        "conditions": {
            "upload_batch_id": upload_batch_id
        }
    }

    response = requests.post(build_product_api_url(uri), headers=json_header(), data=obj2json(params))
    check_response(response)
    response_dict = json2dict(response.text)

    status = response_dict["code"]
    message = response_dict["message"]
    ids = []

    if 'success' == status:
        status = 'uploaded'
        succeed_product_list = response_dict["data"]
        if succeed_product_list:
            ids.extend([{"product_id": p["product_id"], "parent_sku": p['parent_sku']} for p in succeed_product_list])
    else:
        status = 'prepare'

    return product_model.UploadStatus(status, message, ids)


def enable_product_sale(token, product_id_list):
    uri = "/product/enableSale"

    params = {
        "token": token,
        "goods_list": product_id_list,
    }

    response = requests.post(build_product_api_url(uri), headers=json_header(), data=obj2json(params))
    check_response(response)

    response_dict = json2dict(response.text)
    response_data = response_dict["data"]

    code = response_data["code"]
    if code != _SUCCESS_CODE:
        message = response_data["message"]
        errors_list = ""
        if "errors_list" in response_data:
            errors_list = response_data["errors_list"]
        message = message + "; => " + obj2json(errors_list)
        raise exception.BizException(message)


def delete_product(token, product_id_list):
    uri = "/product/deleteGoods"

    params = {
        "token": token,
        "goods_list": product_id_list
    }

    response = requests.post(build_product_api_url(uri), headers=json_header(), data=obj2json(params))
    check_response(response)

    response_dict = json2dict(response.text)
    if response_dict["execute_status"] != _EXECUTE_SUCCESS:
        raise exception.BizException("Delete product failed, {}".format(response_dict["message"]))


def copy_product(src_token, dst_token, start_time, end_time, page=1):
    if not src_token or not dst_token:
        raise ValueError("Invalid token")
    if not start_time or not end_time:
        raise ValueError("Invalid start/end time")

    product_list = get_product_list_by_page(src_token, start_time, end_time, page, 25)
    if not product_list:
        print("No product found")
        return

    dto_list = []
    for product in product_list:
        dto_list.extend(product_model.UploadProductDto.from_product(product))

    logging.info("Found {} product, total sku num {}".format(len(product_list), len(dto_list)))

    max_sku_num_per_time = 300
    if len(dto_list) >= max_sku_num_per_time:
        logging.error("Sku num exceed, max is {}".format(max_sku_num_per_time))
        return

    upload_id = upload_product(dst_token, dto_list)
    print("Upload product succeed, upload id {}".format(upload_id))
    # succeed_product_id_list = get_upload_status_by_batch_id(dst_token, upload_id)


def list_unhandled_order(token, start_time=None, end_time=None):
    if not start_time:
        start_time = (datetime.datetime.now() + datetime.timedelta(days=-60)).strftime("%Y-%m-%d") + " 00:00:00"
    if not end_time:
        end_time = datetime.datetime.today().strftime('%Y-%m-%d') + " 23:59:59"

    uri = "/order/Unhandled?token=" + token + "&start_confirm_time=" + start_time + "&end_confirm_time=" + end_time

    response = requests.get(build_order_api_url(uri))
    check_response(response)

    response_dict = json2dict(response.text)
    if response_dict["code"] != _SUCCESS_CODE:
        raise exception.BizException(response_dict["msg"])

    unhandled_order_list = []
    order_list = response_dict["data"]["order_list"]
    if not order_list:
        return unhandled_order_list

    for order in order_list:
        order_obj = dict2obj(order)

        id = order_obj.order_sn
        type = "普通订单"
        if order_obj.is_combined_order:
            type = "集运订单"
        confirm_time = order_obj.confirm_time
        sn = order_obj.order_sn
        delivery_count_down = ""
        order_cancel_count_down = ""
        order_collection_count_down = ""
        num = int(order_obj.goods_number)
        price = float(order_obj.shop_price)
        total_price = float(order_obj.shop_price_amount)
        pay_status = order_obj.order_state
        image_url = order_obj.sku_image_url
        detail_url = _PRODUCT_DETAIL_PAGE_PREFIX_URL + order_obj.website_goods_id
        sku = order_obj.sku
        remarks = []
        for k, v in order_obj.style_list.items():
            remarks.append("{}: {}".format(k, v))
        remark_str = "<br>".join(remarks)

        o = order_model.Order(id, type, confirm_time, sn, delivery_count_down, order_cancel_count_down,
                              order_collection_count_down, num, price, total_price, pay_status, image_url, detail_url,
                              sku, remark_str)
        unhandled_order_list.append(o)

    return unhandled_order_list


def list_refund_order(user, password, token, cookies, start_time, end_time):
    if not cookies:
        cookies = get_merchant_cookies(user, password)

    url = _MERCHANT_BASE_URL + "/index.php?q=admin/main/refundAudit/index"

    data = {
        "refund_start_time": start_time,
        "refund_end_time": end_time,
        "order_goods_sn": "",
        "recheck_type": ""
    }
    resp = requests.post(url, data=data, headers={"Cookie": cookies, "User-Agent": USER_AGENT})
    check_response(resp)

    soup = BeautifulSoup(resp.text, "lxml")
    refund_audit_tag = soup.find("table", attrs={"class": "refund_audit-table"})
    if not refund_audit_tag:
        return None

    refund_tag_list = refund_audit_tag.find_all("tr", attrs={"data-order_goods_sn": True})
    result = []
    for tag in refund_tag_list:
        tds = tag.find_all("td")
        sn = str(tds[3].a.string).strip()
        apply_refund_time = str(tds[1].string).strip()
        deadline_time = str(tds[4].string).strip()
        sku = str(tds[5].a.div.string).strip()
        image_url = str(tds[5].a.img.attrs['src']).strip()
        refund_reason = ''
        remark = ''

        dto = order_model.RefundOrder(sn, apply_refund_time,deadline_time,sku,image_url,refund_reason,remark)
        result.append(dto)

    return result


def get_merchant_cookies(user, password):
    url = _MERCHANT_BASE_URL + "/index.php?q=admin/main/index/login"

    logging.info("Login with user {}".format(user))

    data = {
        'acct': user,
        'pswd': password,
        'H_sbmt': "yes",
        'verify_code': "",
        'equipment': "Mac/chrome 90.0.4430.212"
    }

    resp = requests.post(url, data=data, verify=False, timeout=20)
    check_response(resp)
    r = json2dict(resp.text)
    if r["code"] == 'ERROR':
        raise Exception(r["msg"])
    cookies = requests.utils.dict_from_cookiejar(resp.cookies)
    cookie = ""
    for name, value in cookies.items():
        cookie += '{0}={1};'.format(name, value)
    return cookie


if __name__ == '__main__':
    logging.basicConfig(level=logging.INFO,
                        format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')

    # copy_product(os.getenv("SRC_TOKEN"), os.getenv("DST_TOKEN"), '2020-12-13 00:00:00', '2020-12-13 23:59:59', 1)
    #list_unhandled_order(os.getenv("SRC_TOKEN"))
    list_refund_order('tt', 'tt', '', os.getenv("COOKIES"), '2021-03-02', None)
