# -*- coding: utf-8 -*-
import math
import time
import requests
import urllib3
import concurrent.futures
from typing import Any, Literal, Mapping
from urllib import error
from tqdm import tqdm
from ddans.common.type import Callback
from ddans.native.hook import NHook
from ddans.native.system import NSystem
from ddans.native.url import NUrl
from ddans.native.log import NLog
from ddans.descriptor.singleton import singleton


@singleton
class SNetter(object):
    _http = None
    _timeout = urllib3.Timeout(total=30)

    def __init__(self):
        self._http = urllib3.PoolManager()
        self._executor = concurrent.futures.ThreadPoolExecutor(10)

    @property
    def http(self):
        return self._http

    def get_headers(self, headers: Mapping[str, str] | None = None):
        return {'Content-Type': 'application/json'} | (headers or {})

    def request(self,
                url: str,
                method: Literal['GET', 'POST'] = 'GET',
                params: Any | None = None,
                transform_body: Callback | None = None,
                headers: Mapping[str, str] | None = None):
        try:
            _url = url

            if method == 'GET':
                _url = '%s%s' % (url, NUrl.url_query(params, '?'))
                resp = self.http.request(method, url=_url, headers=headers)
            else:
                _headers = self.get_headers(headers)
                if transform_body is not None:
                    data = transform_body(params)
                else:
                    data = NHook.json_dumps(params)
                resp = self.http.request(method,
                                         url=_url,
                                         body=data,
                                         headers=_headers)
            if resp.status == 200:
                return resp.data.decode('utf-8') or ''
            else:
                return None
        except error.HTTPError as e:
            NLog.error("request HTTPError %s - %s" % (e.code, e.reason))
            return None
        except Exception as e:
            NLog.error("request error %s" % str(e))
            return None

    def download(self,
                 url: str,
                 dir: str = None,
                 name: str = None,
                 show_pbar=False) -> str | None:
        try:
            if not NHook.isvalid_str(url):
                return None

            _name = name or NSystem.basename(url)
            _dir = dir or NSystem.get_current_path()
            file = NSystem.join(_dir, _name)
            NSystem.ensure_dir(file)

            headers = {
                "Cache-Control": "no-cache"  # 告诉服务器不要缓存
            }

            params = {
                "t": int(time.time())  # 当前时间戳（秒级）
            }
            if show_pbar:
                resp = requests.get(url,
                                    stream=True,
                                    headers=headers,
                                    params=params)
                _total = float(resp.headers.get("Content-Length", 0))
                total = round(_total) / 1024 / 1024
                total = math.ceil(total * 100) / 100
                chunk_size = 1024 * 1024
                with open(file, 'wb') as f:
                    for data in tqdm(iterable=resp.iter_content(chunk_size),
                                     ncols=60,
                                     colour="cyan",
                                     desc="正在下载",
                                     total=total,
                                     unit='MB'):
                        f.write(data)
                return NSystem.get_full_path(file)
            else:
                resp = requests.get(url, headers=headers, params=params)
                with open(file, 'wb') as f:
                    f.write(resp.content)
                return NSystem.get_full_path(file)
        except error.HTTPError as e:
            NLog.error("download HTTPError %s - %s" % (e.code, e.reason))
            return None
        except Exception as e:
            NLog.error("download error %s" % str(e))
            return None

    def fetch(self,
              url: str,
              method: Literal['GET', 'POST'] = 'GET',
              params: Any | None = None,
              transform_data: Callback | None = None,
              headers: Mapping[str, str] | None = None):
        try:
            _url = url
            if method == 'GET':
                _url = '%s%s' % (url, NUrl.url_query(params, '?'))
                future = self._executor.submit(requests.get,
                                               _url,
                                               headers=headers)
            else:
                _headers = self.get_headers(headers)
                if transform_data is not None:
                    data = transform_data(params)
                else:
                    data = NHook.json_dumps(params)
                future = self._executor.submit(requests.post,
                                               _url,
                                               headers=_headers,
                                               data=data)
            resp = future.result()
            if resp.status_code == 200:
                return resp.content
            else:
                return None
        except error.HTTPError as e:
            NLog.error("fetch HTTPError %s - %s" % (e.code, e.reason))
            return None
        except Exception as e:
            NLog.error("fetch error %s" % str(e))
            return None
