# -*- coding: utf-8 -*-
# !/usr/bin/env python
# Software: PyCharm
# __author__ == "YU HAIPENG"
# fileName: request_send_file.py
# Month: 七月
# time: 2021/7/30 11:31
""" Write an introduction to the module here """
import binascii
import os
from io import BytesIO
import filetype
import requests
import six
import hashlib

logger = None

__all__ = ["send_file", "encode_multipart_form_data"]


def get_log():
    global logger
    if logger is None:
        import logging
        logging.basicConfig(
            format='PROCESS ID:%(process)d: %(asctime)s-%(name)s-%(levelname)s '
                   '-[line:%(lineno)d]: %(message)s', level=logging.INFO)
        logger = logging.getLogger('req')
        return logger


def __send(url, data=None, json=None, headers=None, params=None, **kwargs):
    session = kwargs.pop("session", requests)
    send_fun = getattr(session, kwargs.pop("method", "post"))
    try:
        response = send_fun(
            url=url,
            params=params,
            json=json or {},
            data=data or {},
            headers=headers or {"Content-Type": "application/json"},
            timeout=int(kwargs.pop("timeout", 20)),
            **kwargs
        )
    except Exception as e:
        import traceback
        get_log()
        logger.info(traceback.format_exc())
        return
    return response


def send_file(url, files, *args, **kwargs):
    content_type, body, content_length = encode_multipart_form_data(
        kwargs.pop("data", None),
        files=files, file_content_type=kwargs.pop("file_content_type", None)
    )
    headers = kwargs.pop("headers", dict())
    if headers:
        headers = {str(k).title(): v for k, v in headers.items()}
    headers["Content-Type"] = content_type
    headers["Content-Length"] = content_length
    return __send(
        url=url,
        headers=headers,
        data=body,
        *args, **kwargs
    )


def encode_multipart_form_data(data=None, files=None, file_content_type=None):
    """
    data is a sequence of {name:value} elements for regular form fields.
    files is a sequence of {filename:value} elements for data to be
    uploaded as files.
    Return (content_type, body) ready for httplib.HTTP instance
    files
        todo example one:
            多个文件
                [
                    远程接收名字          文件名      文件内容
                    {remote_accpt_name: {file_name: content}},
                    {remote_accpt_name2: {file_name2: content2}},
                    {remote_accpt_name3: (file_name3, file_path or bytes)},
                    {remote_accpt_name4: (file_name3, file_path or bytes, content_type)},
                ]
            单个文件
                远程接收名字          文件名      文件内容
                {remote_accpt_name: {file_name: content}}
                {remote_accpt_name2: (file_name2, file_path or bytes)},
                {remote_accpt_name3: (file_name3, file_path or bytes, content_type)},
        todo example two:
             多个文件
                [
                    远程接收名字          路径
                    {remote_accpt_name: file_path},
                    {remote_accpt_name2: file_path2},
                ]
            单个文件
                远程接收名字          路径
                {remote_accpt_name: file_path }
        file_content_type:
            todo example:
                文件名
                {file_name: application/text}
    data 数据
    """
    data = data or dict()
    file_content_type = file_content_type or dict()
    files = [] if not files else [files] if isinstance(files, dict) else files
    with BytesIO() as body:
        boundary = choose_boundary()
        __write_data(data, boundary, body)
        __write_files(files, boundary, file_content_type, body)
        body.write(f'--{boundary}--\r\n'.encode("utf-8"))
        content_type = 'multipart/form-data;boundary=%s' % boundary
        getvalue = body.getvalue()
    content_length = str(len(getvalue))
    return content_type, getvalue, content_length


def choose_boundary():
    """
    Our embarrassingly-simple replacement for mimetools.choose_boundary.
    """
    boundary = binascii.hexlify(os.urandom(16))
    if six.PY3:
        boundary = boundary.decode('ascii')
    return boundary


def guss_type(path_or_bytes_or_bytearray):
    name = filetype.guess_mime(path_or_bytes_or_bytearray)
    if not name:
        return "application/unknown"
    return name


def __md5_check(md5, clear, md5_check_li=[]):  # noqa
    """

    @param md5:
    @param clear:
    @param md5_check_li:
    @return:
    """
    if clear:
        md5_check_li.clear()
    if md5 not in md5_check_li:
        md5_check_li.append(md5)
        return False
    return True


def __write_data(data, boundary, body):
    for key, value in data.items():  # 处理 data内容
        body.write(f'--{boundary}\r\n'.encode(encoding="utf-8"))
        body.write(
            f'Content-Disposition:form-data;name="{key}"\r\n\r\n'.encode(encoding="utf-8"))
        body.write(f'{value}\r\n'.encode(encoding="utf-8"))


def __write_files(files, boundary, file_content_type, body):
    clear = True
    for file in files:
        for remote_name, value in file.items():  # 处理 文件内容
            if isinstance(value, str):
                if not os.path.isfile(value):
                    raise FileNotFoundError("文件不存在")
                file_name = os.path.basename(value)
                with open(value, 'rb') as fd:
                    content = fd.read()
            elif isinstance(value, dict):
                for file_name, content in value.items():
                    if isinstance(content, bytes):
                        break
                    elif isinstance(content, str):
                        if not os.path.isfile(content):
                            raise FileNotFoundError("文件不存在")
                        with open(content, 'rb') as fd:
                            content = fd.read()
                    else:
                        raise ValueError("content must be bytes or file path")
                    break
            elif isinstance(value, (tuple, list)):
                try:
                    file_name, content = value
                except ValueError:
                    file_name, content, content_type = value
                    file_content_type[file_name] = content_type
                if isinstance(content, str):
                    if not os.path.isfile(content):
                        raise FileNotFoundError("文件不存在")
                    with open(content, 'rb') as fd:
                        content = fd.read()
            else:
                raise ValueError("文件格式不支持")
            if __md5_check(hashlib.md5(content[:10000]).hexdigest(), clear):
                continue
            clear = False
            body.write(f'--{boundary}\r\n'.encode(encoding="utf-8"))
            body.write(f'Content-Disposition: form-data; '
                       f'name="{remote_name}"; '
                       f'filename="{file_name}"'
                       f';filelength="{len(content)}"'
                       f'\r\n'.encode('utf8'))
            body.write(f"Content-Type:{file_content_type.get(file_name) or guss_type(content[:100])}"
                       f"\r\n".encode('utf8'))
            body.write(b"\r\n")
            body.write(content)
            body.write(b"\r\n")