# -*- coding:utf-8 -*-
"""不常用公共函数库"""


def get_python_version(original=False):
    """
    获取Python版本
    :param original: 是否获取原始信息, 直接返回 sys.version
    :return tup: ('3.7.4', '2019-07-08 14:54:52', 'clang-600.0.57')
    """
    # platform.python_version()
    import sys
    version = sys.version
    if original:
        return version
    else:
        from kyger.utility import str_between, date
        from time import mktime, strptime
        return (
            str_between(version, flag=1, end=r' \('),  # 版本号
            date(int(mktime(strptime(str_between(version, ', ', 1, r'\) '), '%b %d %Y, %H:%M:%S'))), '%Y-%m-%d'),
            # 发布日期
            str_between(str_between(version, r'\[', 1, r'\]'), r'\(', 1, r'\)')  # 32/64位
        )


def page_tpl(page, total_page, row, web_url, page_num=5, action='set_page', row_list=[10, 20, 30, 50, 100, 200, 500]):
    """
     分页html生成器，css文件在./py_kyger_cn/template/backend/bluestyle/style目录，需要的样式:
     amazeui.css: [am-cf、am-fr、am-pagination tpl-pagination、am-disabled、am-active]
     app.csss: [ul, li、.tpl-pagination .am-disabled a, .tpl-pagination li a]
     :param page: [int] 当前页码
     :param total_page: [int] 查询到的总页数
     :param row: [int] 每页的记录条数
     :param web_url: [dict] 浏览器请求的url带参数
     :param page_num: [int] 显示多少页
     :param action: [str] 传递到/api/admin_process的action参数
     :param row_list: [list] 每页显示多少条的下拉
     :return: [str] 返回一个分页的html代码块
     return ""div class="am-cf">
                 <div class="am-fr">
                     <ul class="am-pagination tpl-pagination">
                         <li class="am-disabled"><a href="#">«</a></li><li class="am-disabled"><a href="#">‹</a></li>
                         <li class="am-active"><a href="#">1</a></li><li><a href=/article?page=2>2</a></li><li><a href=/article?page=3>3</a></li><li><a href=/article?page=4>4</a></li><li><a href=/article?page=5>5</a></li>
                         <li><a href=/article?page=2>›</a></li><li><a href=/article?page=10>»</a></li>
                     </ul>
                 </div>
             </div>""
     """
    # 通过web_url获取get参数和路径
    total_page = total_page if total_page else 1
    from kyger.utility import url_query2dict, url_analysis, str_between
    url_param = url_query2dict(str_between(web_url, '\?', 3))
    if 'del' in url_param:
        del url_param['del']
    path = str_between(web_url, '\?', 0)

    # 上下页计算
    up_page = 1 if page == 1 else page - 1
    down_page = total_page if page == total_page else page + 1

    # 页码范围计算与设定
    if total_page > page_num:
        start = page - int((page_num - 1) / 2)
        end = page + int(page_num / 2)
        if page - int((page_num - 1) / 2) < 1:
            start = 1
            end = page_num
        if page + int(page_num / 2) > total_page:
            end = total_page
            start = total_page - page_num + 1
    else:
        start = 1
        end = total_page

    # url拼接
    page_url = []  # 每一页的url,倒数第二位为上一页，最后为下一页
    page_list = list(range(start, end + 1))
    page_list.extend([1, up_page, down_page, total_page])
    for i in page_list:
        url_param['page'] = str(i)
        page_url.append(''.join([path, '?%s' % url_query2dict(url_param)]))
    # 分页模板合成
    left_right = '<li%s><a href="%s">%s</a></li><li%s><a href="%s">%s</a></li>'  # 上下首尾页html
    mid_tag = '<li%s><a href="%s">%d</a></li>'  # 页码html

    # 上一页和首页
    if page == 1:
        left = left_right % (' class="am-disabled"', '#', '«', ' class="am-disabled"', '#', '‹')
    else:
        left = left_right % ('', page_url[-4], '«', '', page_url[-3], '‹')

    # 中间页码
    mid = ''
    for i in range(start, end + 1):
        mid += mid_tag % (' class="am-active"', '#', i) if i == page else mid_tag % ('', page_url[i - start], i)

    # 下一页和尾页
    if page == total_page:
        right = left_right % (' class="am-disabled"', '#', '›', ' class="am-disabled"', '#', '»')
    else:
        right = left_right % ('', page_url[-2], '›', '', page_url[-1], '»')

    # 每页显示记录数
    row_html = '<option%s value="/api/admin_process?action=' + action + '&value=%d">%d条/页</option>'
    res = ''
    for i in row_list:
        res += row_html % (' selected="selected"', i, i) if i == row else row_html % ('', i, i)
    html = """<div class="am-cf"><div class="am-fr"><ul class="am-pagination tpl-pagination">%s%s%s<li class="page-row"><select onchange="window.open(this.options[this.selectedIndex].value,'_self')" data-am-selected="{btnSize: 'sm'}" name="menu" class="" style="display: none;" id="selected-search">%s</select></li></ul></div></div> """ % (
        left, mid, right, res)
    return html


def mysql_backup(db):
    """
    数据库备份
    运行 /tools/mysqldump/mysqldump.exe 执行备份
    只备份当前前缀的表
    :return: 备份后的文件名
    """
    from kyger.utility import url_absolute, str_random, date
    from os import system
    exe = url_absolute("tools/mysqldump/mysqldump")
    table = db.cfg["dbname"] + " " + " ".join(db.table_list(log=False))
    file_name = date(format="%Y%m%d%H%M%S") + "_" + str_random(32) + ".sql"
    file = url_absolute("backup/mysql/" + file_name)

    system('%s -h%s -p%s -u%s -p"%s" %s > %s' % (
    exe, db.cfg["host"], db.cfg["port"], db.cfg["user"], db.cfg["password"], table, file))
    return file_name


def mysql_revert(db, file_name):
    """
    数据库还原
    运行 /tools/mysqldump/mysql.exe 执行还原
    :param db: 数据库对象
    :param file_name: 要还原的 SQL 文件名，位于 "/backup/mysql/" 下的一个文件名
    :return:
    """
    from kyger.utility import url_absolute
    from os import system
    exe = url_absolute("tools/mysqldump/mysql")
    file = url_absolute("backup/mysql/" + file_name)
    cmd = '%s -h%s -p%s -u%s -p"%s" --default-character-set=utf8 %s < %s' % (
    exe, db.cfg["host"], db.cfg["port"], db.cfg["user"], db.cfg["password"], db.cfg["dbname"], file)
    db.run_sql("DROP TABLE `" + "`, `".join(db.table_list(log=False)) + "`", log=False)  # 删除所有表
    system(cmd)  # 执行完页面已经挂掉了


def make_pack(source, path, type=0, file_name=""):
    """
    压缩文件函数
    :param source: [str] 要压缩的文件或文件夹
    :param path: [str] 压缩文件保存的文件夹
    :param type: [int] 0压缩为zip格式,1压缩为tar.gz格式
    :param file_name: [str] 文件名,默认为'%Y%m%d%H%M%S_' + 16个随机字符 + '_auto'
    :return:
    """
    import os
    import time
    from kyger.utility import url_absolute, date, str_random, file_list, str_replace, url_relatively, exists
    source = url_relatively(source)
    path = url_relatively(path)
    if exists(url_absolute(source)) and exists(url_absolute(path)):
        path_type = "dir" if os.path.isdir(url_absolute(source)) else "file"
        name = source.split('/')
        if not file_name: file_name = date(int(time.time()), '%Y%m%d%H%M%S_' + str_random(16)) + '_auto'  # 文件名
        if type:  # tar打包
            import tarfile
            tar = tarfile.open(url_absolute(path) + '/' + file_name + '.tar.gz', 'w:gz')
            tar.add(url_absolute(source), arcname=name[len(name) - 1])
            tar.close()

        else:  # zip
            import zipfile
            z = zipfile.ZipFile(url_absolute(path) + '/' + file_name + '.zip', 'w', zipfile.ZIP_STORED)  # 打包，zipfile.ZIP_STORED是默认参数
            if path_type == "file": z.write(url_absolute(source), arcname=name[len(name) - 1])
            else:
                file_list = file_list(source, 0, 1, True)
                for row in file_list:
                    z.write(url_absolute(row), arcname=name[len(name) - 1] + str_replace(row, source, ''))
            z.close()


def un_pack(source, path):
    """
    解压zip，tar.gz文件
    :param source: [str] 要解压的文件
    :param path: [str] 解压的路径
    :return:
    """
    from kyger.utility import url_absolute, file_list, str_replace, url_relatively, exists, file_mime_type
    if exists(url_absolute(source)) and exists(url_absolute(path)):
        if file_mime_type(url_absolute(source)) == "application/zip":  # zip解压
            import zipfile
            file = zipfile.ZipFile(url_absolute(source))
            file.extractall(url_absolute(path))
            file.close()
        elif file_mime_type(url_absolute(source)) == "application/x-tar":  # tar解压
            import tarfile
            file = tarfile.open(url_absolute(source), "r:gz")
            names = file.getnames()
            for name in names:
                file.extract(name, url_absolute(path))
        else:
            return "file erro"


def pack_get(source, path):
    """
    从zip或tar.gz压缩包中获取文件
    :param source: [str] 压缩文件的路径，注意带文件名和后缀名
    :param path: [str] 要获取的文件路径，压缩包内的文件路径，注意带文件名和后缀名
    :return:
    """
    from kyger.utility import url_absolute, file_mime_type
    from zipfile import ZipFile
    import tarfile
    if file_mime_type(source) == "application/zip":  # zip
        myzip = ZipFile(url_absolute(source))
        data = myzip.open(path).read().decode()
    elif file_mime_type(source) == "application/x-tar":  # tar
        mytar = tarfile.open(url_absolute(source), "r:gz")
        data = mytar.extractfile(path).read().decode()
    else:
        data = ''
    return data


# def get_contents(url, mode='rb', header={}, data={}, charset="utf-8", method='GET'):
#     """
#     将一个文件或网址读入到字符串中
#     类似PHP的 file_get_contents 方法
#     :param url: [str] 文件路径或网址URL
#     :param mode: [str] 打开方式: w以写方式打开, wb以二进制写模式打开
#     :param header: [dict] 请求头：{"Accept-Encoding": "", "": ""} Accept-Encoding 为空不压缩，压缩时部分站点会乱码
#     :param data: [dict] 请求数据： {"name": "zh-CN"}
#     :param charset: [str] 字符编码，为空时自动不转换
#     :param method: [str] 请求方式
#     :return: [str]
#     :get_contents("https://www.qq.com", header={"Accept-Encoding": ""}, charset="gbk")
#     """
#     from kyger.utility import url_query2dict, url_absolute
#     from urllib.request import urlopen, Request
#     from urllib.parse import urlencode
#     from ssl import _create_unverified_context  # 不验证SSL证书
#     try:
#         if url.find('://') > 0:  # http
#             if method == 'GET':
#                 if data: data = url_query2dict(data).encode('utf-8')
#                 req = Request(url, data, header, method="GET")
#                 r = urlopen(url=req, context=_create_unverified_context()).read()
#             elif method == 'POST':
#                 postdata = urlencode(data).encode('utf-8')
#                 req = Request(url, postdata, header, method='POST')
#                 r = urlopen(url=req, context=_create_unverified_context()).read()
#             else:
#                 return 'method is not post or get'
#         else:  # 本地
#             url = url_absolute(url)  # 转物理路径
#             fp = open(url, mode)
#             r = fp.read()
#             fp.close()
#         if charset: r = r.decode(encoding=charset, errors="strict")
#     except (FileNotFoundError, PermissionError, Exception) as e:
#         r = str(e)
#     return r

# zhuyinyin:20191260


# def get_contents(url, mode='rb', header={}, data={}, charset="utf-8", method='GET'):
#     """
#     将一个文件或网址读入到字符串中
#     类似PHP的 file_get_contents 方法
#     :param url: [str] 文件路径或网址URL
#     :param mode: [str] 打开方式: w以写方式打开, wb以二进制写模式打开
#     :param header: [dict] 请求头：{"Accept-Encoding": "", "": ""} Accept-Encoding 为空不压缩，压缩时部分站点会乱码
#     :param data: [dict] 请求数据： {"name": "zh-CN"}, POST请求时此为必须参数,GET请求给出data会将data拼接在url中
#     :param charset: [str] 字符编码，为空时自动不转换
#     :param charset: [str] 请求方式, 默认为GET请求
#     :return: [str]
#     :get_contents("https://www.qq.com", header={"Accept-Encoding": ""}, charset="gbk")
#     """
#     from urllib.request import urlopen, Request
#     from kyger.utility import url_query2dict
#     from ssl import _create_unverified_context  # 不验证SSL证书
#     try:
#         if url.find('://') > 0:  # http
#             if method == 'POST':
#                 data = '%s' % data
#                 data = data.replace('\'', '\"')
#                 r = urlopen(url, data.encode('utf-8')).read()
#             else:
#                 if data:
#                     data = url_query2dict(data)
#                     url = url + '&' + data if '?' in url else url + '?' + data
#                 if header == {}:
#                     header = None
#                 req = Request(url, header, method="GET")
#                 r = urlopen(url=req, context=_create_unverified_context()).read()
#         else:  # 本地
#             url = url_absolute(url)  # 转物理路径
#             fp = open(url, mode)
#             r = fp.read()
#             fp.close()
#         if charset:
#             r = r.decode(encoding=charset, errors="strict")
#     except (FileNotFoundError, PermissionError, Exception) as e:
#         r = str(e)
#     return r


def sms(phone, code):
    """
    发送短信函数
    :param phone: [str] 接受短信的手机号码
    :param code: [str] 短信发送的验证码
    :return:
    """
    from kyger.parser_ini import Config
    from kyger.utility import json2dict
    import uuid
    import datetime
    import hmac
    import base64
    from urllib.parse import quote

    send = Config('/config/settings.ini').get('send')
    url = send.get('url', '')  # https://dysmsapi.aliyuncs.com
    data = {
        "AccessKeyId": send.get('id', ''),
        "Action": "SendSms",
        "Format": "JSON",
        "PhoneNumbers": phone,
        "RegionId": "cn-hangzhou",
        "SignName": send.get('sign', ''),
        "SignatureMethod": "HMAC-SHA1",
        "SignatureNonce": str(uuid.uuid4()),
        "SignatureVersion": "1.0",
        "TemplateCode": "SMS_161975030",
        "TemplateParam": '{"code":"%s"}' % code,
        "Timestamp": datetime.datetime.utcnow().isoformat("T"),
        "Version": "2017-05-25"
    }
    # 拼接参数
    param_list = []
    for row in data:
        param_list.append('%s=%s' % (quote(row, safe="~"), quote(data[row], safe="~")))
    query_string = quote('&'.join(param_list), safe="~")

    # 加密Signature
    tosign = "GET&%2F&" + query_string
    secret = send.get('secret', '') + "&"
    hmb = hmac.new(secret.encode("utf-8"), tosign.encode("utf-8"), "sha1").digest()
    Signature = quote(base64.standard_b64encode(hmb).decode("ascii"), safe="~")
    param_list.append('%s=%s' % ('Signature', Signature))

    # 完整url
    url = url + '?' + '&'.join(param_list)

    res = json2dict(get_contents(url))
    return res  # api返回的消息


def xml_data(data):
    """
    xml转字典
    :param data: [str] xml的数据
    :return:
    """
    import re
    data = str(data)
    xml_list = re.findall(r'<xml>([\d\D]*?)</xml>', data)
    data_dict = {}
    for row in xml_list:
        param_list = re.findall(r"<\w*>.*?</\w*>", row)
        for var in param_list:
            if re.search(r'<!\[CDATA\[.*]]>', var):
                key_value = re.search(r'<(\w*)><!\[CDATA\[(.*)]]></\w*>', var)
                data_dict[key_value.group(1)] = key_value.group(2)
            else:
                key_value = re.search(r'<(\w*)>(.*?)</\w*>', var)
                data_dict[key_value.group(1)] = int(key_value.group(2))
    return data_dict


def csv_write(path, headers, rows):
    """
    csv写入函数
    :param path: [str] 文件路径，带csv后缀
    :param headers: [list] 表头，例：['编号', '课程', '讲师']
    :param rows: [list] 每行数据，例：[(1, 'Python', 'fighter.Lu'),(2, 'C#', 'fighter.Lu'),(3, '.Net', 'fighter.Lu')]
    :return:
    """
    from kyger.utility import url_absolute
    import csv
    with open(url_absolute(path), 'w', encoding='utf-8', newline='') as f:
        writer = csv.writer(f)
        writer.writerow(headers)
        writer.writerows(rows)


def csv_read(path):
    """
    读取csv文件函数
    :param path: [str] csv文件路径
    :return: {"header": 头, "rows": 每行内容列表套元组}
    """
    import csv
    from kyger.utility import url_absolute

    try:
        with open(url_absolute(path), encoding='utf-8') as f:
            reader = csv.DictReader(f)
            header, rows = ([], [])
            for row in reader:
                line = []
                for key in row:
                    if key not in header: header.append(key)
                    line.append(row[key])
                rows.append(tuple(line))
    except Exception as result:
        with open(url_absolute(path), encoding='gbk') as f:
            reader = csv.DictReader(f)
            header, rows = ([], [])
            for row in reader:
                line = []
                for key in row:
                    if key not in header: header.append(key)
                    line.append(row[key])
                rows.append(tuple(line))
    finally:
        return {"header": header, "rows": rows}


def get_contents(url, mode='rb', header={}, data=None, charset="utf-8", method="GET"):
    """
    将一个文件或网址读入到字符串中
    类似PHP的 file_get_contents 方法
    :param url: [str] 文件路径或网址URL
    :param mode: [str] 打开方式: w以写方式打开, wb以二进制写模式打开
    :param header: [dict] 请求头：{"Accept-Encoding": "", "": ""} Accept-Encoding 为空不压缩，压缩时部分站点会乱码
    :param data: [dict] 请求数据： {"name": "zh-CN"}
    :param charset: [str] 字符编码，为空时自动不转换
    :param method：请求方法，GET 或 POST
    :return: [str]
    :get_contents("https://www.qq.com", header={"Accept-Encoding": ""}, charset="gbk")
    """
    from urllib.request import urlopen, Request
    import urllib.parse
    from ssl import _create_unverified_context  # 不验证SSL证书
    try:
        if url.find('://') > 0:  # http
            if data:
                data = url_query2dict(data).encode("utf-8") if method == "GET" else urllib.parse.urlencode(data).encode()
            req = Request(url, data, header, method=method)
            r = urlopen(url=req, context=_create_unverified_context()).read()
        else:  # 本地
            url = url_absolute(url)  # 转物理路径
            fp = open(url, mode)
            r = fp.read()
            fp.close()
        if charset: r = r.decode(encoding=charset, errors="strict")
    except (FileNotFoundError, PermissionError, Exception) as e:
        r = str(e)
    return r


def permission(db, kg):
    """
    权限检测函数
    :param db: [object] 数据库操作对象
    :param kg: [dict] 全局变量
    :return:
    """
    from kyger.utility import json2dict, str_replace
    from kyger.article import Article
    from kyger.product import Product
    from kyger.download import Download
    from kyger.picture import Picture
    from kyger.research import Research

    # 获取用户的等级
    user_rank = kg['user'].get('level', [])
    if not user_rank:
        rank = 0
    else:
        where = 'webid=%s && id in (%s)' % (kg['web']['id'], '%s'.join('%s' % v for v in user_rank))
        res = db.list(table='rank', where=where, shift=1)
        rank = res['id'] if res else 0

    # 获取数据
    module = kg['model'][1]  # 请求的模块
    module = str_replace(module, '_mobile', '')
    aid = kg['get'].get('id', 0)  # 访问内容页的id
    cid = kg['get'].get('cid', 0)  # 访问列表页的id

    # 栏目时的field
    field = "*, from_unixtime(`addtime`, '%Y-%m-%d %H:%i:%s') as `date`"
    if aid:
        # 获取数据
        if module == 'article':
            data = Article(db, kg).single(aid, '%Y-%m-%d %H:%M:%S', 1)
        elif module == 'research' or module == 'research_article':
            data = Research(db, kg).single(aid, 0, '%Y-%m-%d %H:%M:%S', 1)
        elif module == 'photo':
            data = Picture(db, kg).single(aid, '%Y-%m-%d %H:%M:%S', 1)
        elif module == 'download':
            data = Download(db, kg).single(aid, '%Y-%m-%d %H:%M:%S', 1)
        elif module == 'product':
            data = Product(db, kg).single(aid, '%Y-%m-%d %H:%M:%S', 1)
        else:
            data = {}
        # 没有数据
        if not data:
            return {'status': 404}
        # 判断该内容有无权限
        if data.get('authority'):
            if rank not in data['authority'] and 0 not in data['authority']:
                return {'status': 1}
        # 判断上级栏目是否有权限
        where = 'id in (%s)' % ','.join('%s' % v for v in data['category'])
        upper = db.list(table='category', where=where)

        for row in upper:
            if rank not in json2dict(row['permission']) and 0 not in json2dict(row['permission']):
                return {'status': 1}
        return {'status': 0, 'data': data, 'rank': rank}

    elif cid:

        data = db.list(table='category', field=field, where='webid=%s && id=%s' % (kg['web']['id'], cid), shift=1)
        if not data:
            return {'status': 404}
        # 判断栏目本身有没有权限
        data['upper'] = json2dict(data['upper'])
        if rank not in json2dict(data['permission']) and 0 not in json2dict(data['permission']):
            return {'status': 1}
        # 判断父级栏目有没有权限
        if data['level'] > 1:
            upper = db.list(table='category', where="id in (%s)" % ','.join('%s' % v for v in data['upper']))
            for row in upper:
                if rank not in json2dict(row['permission']) and 0 not in json2dict(row['permission']):
                    return {'status': 1}
        return {'status': 0, 'data': data, 'rank': rank}
    else:
        where = 'module="%s" && level=1 && enable=1 && webid=%s' % (module, kg['web']['id'])
        data = db.list(table='category', field=field, where=where, shift=1)
        if not data:
            return {'status': 404}
        # 判断是否有权限
        data['upper'] = json2dict(data['upper'])
        if rank not in json2dict(data['permission']) and 0 not in json2dict(data['permission']):
            return {'status': 1}
        return {'status': 0, 'data': data, 'rank': rank}


def check_del_material(db, del_list):
    """
    验证删除的素材id列表是否有已经被引用的素材
    @param del_list: 要删除的素材id列表
    @return: del_id: 可以删除的元素id列表
            udel_list:无法删除的元素id列表
    """
    from kyger.utility import numeric, dict2json_file
    # 关键词回复表
    key_list = db.list(table='wx_questions', field="`mid`")

    # 其他表
    # other_list = self.db.list(table='other_table', field="`id`")

    udell_list = []  # 无法删除的素材id
    for row in range(len(del_list)):
        # 判断要删除的本地素材id是否被关键词回复表引用
        if numeric(del_list[row]) in list(map(lambda x: x['mid'], key_list)):
            # 被引用将无法删除
            udell_list.append(del_list[row])

        # 判断要删除的本地素材id是否被其他表引用
        # if numeric(del_list[row]) in list(map(lambda x: x['mid'], other_list)):
        # 被引用将无法删除
        # udell_list.append(del_id[row])

    # 删除id中无法删除的id
    if udell_list:
        for j in udell_list:
            del_list.remove(j)
    return del_list, udell_list
