#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Author: 河北雪域网络科技有限公司 A.Star
# @contact: astar@snowland.ltd
# @site: www.snowland.ltd
# @file: _string.py
# @time: 2019/6/3 11:43
# @Software: PyCharm


__author__ = 'A.Star'

import datetime
import re

import six

from astartool.number import ishex
from astartool.random import random_digit_string
from astartool.common import _PROTECTED_TYPES


is_hex = ishex

__all__ = [
    'is_email',
    'is_mobile',
    'is_ip',
    'is_url',
    'generate_number',
    'check_number',
    'force_bytes',
    'force_str',
    'to_binary',
    'to_text',
    'has_Chinese'
]


def is_email(email):
    """
    判断字符串是否为email
    :param email:
    :return:
    """
    regex = r'^[0-9a-zA-Z_\-\.]{0,19}@[0-9a-zA-Z_\-]{1,13}\.[a-zA-Z\.]{1,20}$'
    return re.match(regex, email) is not None


def is_ip(ip):
    """
    检验IP是否合法
    :param ip:
    :return:
    """
    compile_ip = re.compile(
        '^(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|[1-9])\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)$')
    return compile_ip.match(ip) is not None


def is_mobile(mobile):
    """
    判断是不是手机号
    :param mobile:
    :return:
    """
    regex = r'^1[0-9]{10}$'
    return re.match(regex, mobile) is not None


def is_url(url):
    """
    参考 https://blog.csdn.net/qq_20446879/article/details/86539414
    """
    regex = "^((https|http|ftp|rtsp|mms)?://)?(([0-9a-z_!~*'().&=+$%-]+: )" \
            "?[0-9a-z_!~*'().&=+$%-]+@)?(([0-9]{1,3}\\.){3}[0-9]{1,3}" \
            "|" \
            "([0-9a-z_!~*'()-]+\\.)*" \
            "([0-9a-z][0-9a-z-]{0,61})?[0-9a-z]\\.[a-z]{2,6})(:[0-9]{1,5})?((/?)|(/[0-9a-z_!~*'().;?:@&=+$,%#-]+)+/?)$"
    return re.match(regex, url.lower()) is not None


def is_protected_type(obj):
    """Determine if the object instance is of a protected type.

    Objects of protected types are preserved as-is when passed to
    force_str(strings_only=True).
    """
    return isinstance(obj, _PROTECTED_TYPES)


def generate_number(k: int = 18):
    """
    k前14位是时间，后2位校验，中间是随机数。随机数应该不少于2位
    :param k:
    :return:
    """
    assert k >= 18, "generate number 18 chars at least"
    assert not (k & 1), "generate number mast be odd"
    time_str = datetime.datetime.now().strftime("%Y%m%d%H%M%S")  # 14位
    rand_k = k - 16
    middle = random_digit_string(rand_k)  # 至少4位
    header = time_str + middle
    a1 = sum([int(i) for i in header[::2]]) % 10
    a2 = sum([int(i) for i in header[1::2]]) % 10
    c1 = str((-a1) % 10)
    c2 = str((-a2) % 10)
    return header + c1 + c2


def has_Chinese(text: str):
    """
    判断是否存在中文字符
    """
    return re.match('.*?[\u4e00-\u9fff].*?', text) is not None


def check_number(number):
    return not ((sum((int(i) for i in number[::2])) % 10) and (sum((int(i) for i in number[1::2])) % 10))


def force_bytes(s, encoding='utf-8', errors='strict'):
    """
    Similar to smart_bytes, except that lazy instances are resolved to
    strings, rather than kept as lazy objects.

    If strings_only is True, don't convert (some) non-string-like objects.
    """
    # Handle the common case first for performance reasons.
    if isinstance(s, bytes):
        if encoding == 'utf-8' or encoding == 'utf8':
            return s
        else:
            return s.decode('utf-8', errors).encode(encoding, errors)
    if isinstance(s, memoryview):
        return bytes(s)
    else:
        return s.encode(encoding, errors)


def force_str(s, encoding='utf-8', strings_only=False, errors='strict'):
    """
    Similar to smart_str(), except that lazy instances are resolved to
    strings, rather than kept as lazy objects.

    If strings_only is True, don't convert (some) non-string-like objects.
    """
    # Handle the common case first for performance reasons.
    if issubclass(type(s), str):
        return s
    if strings_only and is_protected_type(s):
        return s
    try:
        if isinstance(s, bytes):
            s = str(s, encoding, errors)
        else:
            s = str(s)
    except UnicodeDecodeError as e:
        raise e
    return s


def to_text(value, encoding='utf-8'):
    """Convert value to unicode, default encoding is utf-8

    :param value: Value to be converted
    :param encoding: Desired encoding
    """
    if not value:
        return ''
    if isinstance(value, six.text_type):
        return value
    if isinstance(value, six.binary_type):
        return value.decode(encoding)
    return six.text_type(value)


def to_binary(value, encoding='utf-8'):
    """Convert value to binary string, default encoding is utf-8

    :param value: Value to be converted
    :param encoding: Desired encoding
    """
    if not value:
        return b''
    if isinstance(value, six.binary_type):
        return value
    if isinstance(value, six.text_type):
        return value.encode(encoding)
    return to_text(value).encode(encoding)
