# -*- coding: utf-8 -*-
import requests
import json
import time
from urllib import parse
from base_framework.public_tools.get_token import LoginSys, SparkleLogin, AgentApiLogin, MarketApiLogin, token_dict, \
    AllSchool, \
    ParentLogin, student_token_cache, StudentLogin, SparkEduLogin, SparkSaasLogin, SparkSaasTeacherLogin
from base_framework.public_tools import log
from base_framework.public_tools.read_config import ReadConfig, get_current_env
from base_framework.base_config.current_pth import *
from base_framework.public_tools.read_config import get_current_config
from base_framework.public_tools.sqlhelper import MySqLHelper

obj_log = log.get_logger()
evn_cfg = ReadConfig(env_choose_path)
base_cfg = ReadConfig(config_file_path)
all_school_api_host = ['api.qa.allschool.com', 'api.sim.allschool.com', 'api.allschool.com']


class Runner(LoginSys):
    def __init__(self):
        super().__init__()
        # self.env = get_current_config(section="run_evn_name", key="current_evn")
        self.env = get_current_env()
        self.is_need_sso = ["teach-api"]
        self.team = get_current_config(section="run_evn_name", key="current_team")
        self.db_con = MySqLHelper()
        self.sql_data = []  # 存放接口响应时间
        self.week = time.strftime("%w", time.localtime())

    def __new__(cls, *args, **kwargs):

        if not hasattr(cls, 'instance'):
            cls.instance = super().__new__(cls)

        return cls.instance

    def __del__(self):
        if self.sql_data:  # 如果有数据未插入
            # sql_str = ("insert into sparkatp.interface_response_time(team, in_type, rp_time, in_url,in_id) "
            #            "values (%s,%s,%s,%s,%s);")
            # self.db_con.insert_many(sql=sql_str, param=self.sql_data)
            # self.sql_data.clear()
            pass

    def __call_api(self, session, api_url, req_type, **kwargs):
        try:
            if req_type == "GET":
                req = session.get(api_url, verify=False, **kwargs)
            elif req_type == "POST":
                req = session.post(api_url, verify=False, **kwargs)
            elif req_type == "PUT":
                req = session.put(api_url, verify=False, **kwargs)
            elif req_type == "DELETE":
                req = session.delete(api_url, verify=False, **kwargs)
            elif req_type == "PATCH":
                req = session.patch(api_url, verify=False, **kwargs)
            else:
                obj_log.info(f'req_type,输入错误！不支持请求方法{req_type}')
                raise Exception(f'req_type,输入错误！不支持请求方法{req_type}')
        except Exception as e:
            obj_log.info('返回数据：{}'.format(e))
            return e

        if self.env.lower() != "sim":  # 非sim环境才记录接口响应时间
            # 每周六，记录一次所有接口的响应时间
            if self.week in ['6']:
                current_time = int(time.strftime("%H", time.localtime()))
                if current_time >= 5:  # 5点之前的404校验脚本不统计，因为都是非正常参数，详见：http://10.250.200.1:8080/jenkins/view/2-%E8%BE%85%E5%8A%A9%E7%A8%8B%E5%BA%8F/job/%E6%8E%A5%E5%8F%A3404%E6%A3%80%E6%9F%A5/
                    run_time = int(req.elapsed.total_seconds() * 1000)
                    if run_time > 200:  # 超过200ms的接口记录到数据库
                        in_data = (self.team, req_type, run_time, api_url, 0)
                        self.sql_data.append(in_data)
                        if len(self.sql_data) >= 10:  # 每10条数据插入一次, 最后一次不足100条时在__del__中插入
                            sql_str = (
                                "insert into sparkatp.interface_response_time(team, in_type, rp_time, in_url,in_id) "
                                "values (%s,%s,%s,%s,%s);")
                            self.db_con.insert_many(sql=sql_str, param=self.sql_data)
                            self.sql_data.clear()
        return req

    def call_rest_api(self, API_URL, req_type, user=None,
                      token=None, current_evn=None, is_file=False, **kwargs):
        '''
        功能：所有接口访问入口
        '''
        if self.env.lower() == "sim":
            API_URL = API_URL.replace(".qa.", ".sim.")
        return self._call_zhyy_api(API_URL, req_type, user,
                                   token, current_evn, is_file, **kwargs)

    def _call_zhyy_api(self, api_url, req_type, user=None, token=None, current_evn=None, is_file=False, **kwargs):
        """
        功能：模拟走ruoyi登录模式，带token访问接口
        :param API_URL:
        :param req_type:
        :param user: 使用的自定义用户登录
        :param token: 从SSO获取的token，此字段使用默认值None，不是的情况使用False
        :param current_evn: QA or SIM
        :param is_file:
        :param kwargs:
        :return:
        """
        req_type = req_type.upper()
        retry_num = 4
        cnt = 0
        if 'as_login_type' in kwargs.keys():
            kwargs.pop('as_login_type')
        options_dict = dict(**kwargs)
        result = parse.urlparse(url=api_url, allow_fragments=True)
        host = result.hostname
        if not user:
            user = self.curt_user
        obj_log.info('登录系统为{}，用户名为手动输入：{}'.format(host, user))
        obj_log.info('请求地址：{}'.format(api_url))
        obj_log.info('请求数据：{}'.format(options_dict))
        header = {"Authorization": "Basic c3BhcmtsZS13ZWI6c3BhcmtsZS13ZWI=", "tenant-id": "1"}
        session = requests.session()
        session.headers.update(header)
        while cnt < retry_num:
            if token is None:
                if not user:
                    user = evn_cfg.get_value(sections='run_user_name', options='default_user')
                tenant_name = base_cfg.get_value(sections=user, options='tenant')
                usr_name = base_cfg.get_value(sections=user, options='username')
                usr_pwd = base_cfg.get_value(sections=user, options='password')
                # 检查 kwargs 中是否包含登录信息（tenantName/username 和 password）
                # 如果包含，使用传入的参数；否则使用配置文件中的登录信息
                json_data = kwargs.get('json', {})
                is_login_params = isinstance(json_data, dict) and 'password' in json_data and (
                            'tenantName' in json_data or 'username' in json_data)

                if is_login_params:
                    # 使用传入的登录参数
                    obj_log.info('检测到 kwargs 中包含登录信息，使用传入的参数进行登录')
                    login_req = self.__call_api(session, api_url, req_type='POST', **kwargs)
                else:
                    # 使用配置文件中的登录信息
                    obj_log.info('使用配置文件中的登录信息进行登录')
                    request_body = {'json': {'tenantName': tenant_name, 'username': usr_name, 'password': usr_pwd,
                                             'rememberMe': 'true'}}
                    api_login_url = self.zhyy_host
                    login_req = self.__call_api(session, api_login_url, req_type='POST', **request_body)
                try:
                    req_json = login_req.json()
                except Exception:
                    # 如果 json() 失败，尝试将 text 转成 JSON
                    try:
                        if isinstance(login_req, Exception):
                            obj_log.warning("登录请求失败: {}".format(login_req))
                            req_json = {}
                        else:
                            req_json = json.loads(login_req.text)
                    except Exception:
                        if isinstance(login_req, Exception):
                            obj_log.warning("登录请求失败: {}".format(login_req))
                        else:
                            obj_log.warning("登录响应无法解析为JSON: {}".format(login_req.text))
                        req_json = {}
                # 判断登录是否成功：code为0或200表示成功，或者data中包含accessToken
                login_code = req_json.get("code")
                if login_code not in [0, 200] and not req_json.get("data", {}).get('accessToken'):
                    raise Exception("SSO登录失败: {}".format(req_json))
                user_token = req_json.get("data", {}).get('accessToken')
                # 如果 api_url 本身就是登录接口，登录成功后直接返回登录响应
                if is_login_params:
                    obj_log.info('api_url 是登录接口，直接返回登录响应')
                    if not is_file:
                        return req_json
                    else:
                        return login_req.content
            else:
                user_token = token
            if user_token:
                session.headers.update({'ssotoken': user_token})
                session.headers.update({'sso-token': user_token})
                session.headers.update({'accesstoken': user_token})
                session.headers.update({'Accesstoken': user_token})
                session.headers.update({'access-token': user_token})
                session.headers.update({'Authorization': user_token})
                session.headers.update({'token': user_token})
            obj_log.info(f'请求头headers：{session.headers}')
            req = self.__call_api(session, api_url, req_type, **kwargs)
            if isinstance(req, Exception):
                obj_log.error('请求失败：{}'.format(req))
                return req
            if not is_file:
                try:
                    rtn_temp = req.json()
                except Exception as e:
                    # 如果 json() 失败，尝试将 text 转成 JSON
                    try:
                        rtn_temp = json.loads(req.text)
                    except Exception:
                        # 如果都失败，保持为 text 字符串
                        rtn_temp = req.text
            else:
                rtn_temp = req.content
            # SSO登录过期处理开始
            status_code = str(req.status_code)
            obj_log.info('------状态码：{}， 返回信息：{}'.format(status_code, rtn_temp))
            try:
                # 如果 rtn_temp 是字典，才能使用 .get() 方法
                if isinstance(rtn_temp, dict):
                    resp_code = str(rtn_temp.get('code', '200'))
                else:
                    resp_code = '200'
            except:
                resp_code = '200'
                if not is_file:
                    token_dict.clear()  # 缓存session过期清理
                    student_token_cache.clear()  # 缓存session过期清理
                    obj_log.warning("--缓存session过期，清理缓存！")
                    cnt += 1
                    if cnt <= 1:
                        continue
            if status_code == "401" or resp_code == '401':
                token_dict.clear()  # 缓存session过期清理
                student_token_cache.clear()  # 缓存session过期清理
                obj_log.warning("缓存session过期，清理缓存！")
                cnt += 1
                if cnt <= 1:
                    continue
            if status_code == "200" and "<!DOCTYPE html>" in str(rtn_temp):
                token_dict.clear()  # 缓存session过期清理
                student_token_cache.clear()  # 缓存session过期清理
                obj_log.warning("--缓存session过期，清理缓存！")
                cnt += 1
                if cnt <= 1:
                    continue
            # SSO登录过期处理完成
            req.close()
            if not is_file:
                obj_log.info('返回数据：{}'.format(rtn_temp))
            else:
                obj_log.info('返回数据：文件字节流')
            return rtn_temp
        return False

    def _call_sso_api(self, api_url, req_type, user=None, token=None, current_evn=None, is_file=False, **kwargs):
        """
        功能：模拟走sso登录模式，带token访问接口
        :param API_URL:
        :param req_type:
        :param user: 使用的自定义用户登录
        :param token: 从SSO获取的token，此字段使用默认值None，不是的情况使用False
        :param current_evn: QA or SIM
        :param is_file:
        :param kwargs:
        :return:
        """
        req_type = req_type.upper()
        retry_num = 4
        cnt = 0
        if 'as_login_type' in kwargs.keys():
            kwargs.pop('as_login_type')
        options_dict = dict(**kwargs)
        result = parse.urlparse(url=api_url, allow_fragments=True)
        host = result.hostname
        if not user:
            user = self.curt_user
        obj_log.info('登录系统为{}，用户名为手动输入：{}'.format(host, user))
        obj_log.info('请求地址：{}'.format(api_url))
        obj_log.info('请求数据：{}'.format(options_dict))

        header = {"Authorization": "Basic c3BhcmtsZS13ZWI6c3BhcmtsZS13ZWI=", "tenant-id": "1"}
        session = requests.session()
        session.headers.update(header)
        while cnt < retry_num:
            if token is None:
                if not user:
                    user = evn_cfg.get_value(sections='run_user_name', options='default_user')
                usr_name = base_cfg.get_value(sections=user, options='show_username')
                usr_pwd = base_cfg.get_value(sections=user, options='password')
                sso_url = "{}?username={}&password={}".format(self.sparkle_pc_token_url, usr_name, usr_pwd)
                sso_rsp = session.post(sso_url).json()
                if sso_rsp.get("code") != 200:
                    raise Exception("SSO登录失败: {}".format(sso_rsp))
                user_token = sso_rsp.get("data").get('accessToken')
            else:
                user_token = token
            if user_token:
                session.headers.update({'ssotoken': user_token})
                session.headers.update({'sso-token': user_token})
                session.headers.update({'accesstoken': user_token})
                session.headers.update({'Accesstoken': user_token})
                session.headers.update({'access-token': user_token})
                session.headers.update({'token': user_token})
            obj_log.info(f'请求头headers：{session.headers}')

            req = self.__call_api(session, api_url, req_type, **kwargs)

            if not is_file:
                try:
                    rtn_temp = req.json()
                except Exception as e:
                    # 如果 json() 失败，尝试将 text 转成 JSON
                    try:
                        rtn_temp = json.loads(req.text)
                    except Exception:
                        # 如果都失败，保持为 text 字符串
                        rtn_temp = req.text
            else:
                rtn_temp = req.content
            # SSO登录过期处理开始
            status_code = str(req.status_code)
            obj_log.info('------状态码：{}， 返回信息：{}'.format(status_code, rtn_temp))
            try:
                # 如果 rtn_temp 是字典，才能使用 .get() 方法
                if isinstance(rtn_temp, dict):
                    resp_code = str(rtn_temp.get('code', '200'))
                else:
                    resp_code = '200'
            except:
                resp_code = '200'
                if not is_file:
                    token_dict.clear()  # 缓存session过期清理
                    student_token_cache.clear()  # 缓存session过期清理
                    obj_log.warning("--缓存session过期，清理缓存！")
                    cnt += 1
                    if cnt <= 1:
                        continue

            if status_code == "401" or resp_code == '401':
                token_dict.clear()  # 缓存session过期清理
                student_token_cache.clear()  # 缓存session过期清理
                obj_log.warning("缓存session过期，清理缓存！")
                cnt += 1
                if cnt <= 1:
                    continue
            if status_code == "200" and "<!DOCTYPE html>" in str(rtn_temp) and "https://sso.qa.huohua.cn" in str(
                    rtn_temp):
                token_dict.clear()  # 缓存session过期清理
                student_token_cache.clear()  # 缓存session过期清理
                obj_log.warning("--缓存session过期，清理缓存！")
                cnt += 1
                if cnt <= 1:
                    continue
            # SSO登录过期处理完成
            req.close()
            if not is_file:
                obj_log.info('返回数据：{}'.format(rtn_temp))
            else:
                obj_log.info('返回数据：文件字节流')
            return rtn_temp

        return False


if __name__ == '__main__':
    obj_runner = Runner()
    # url = "https://api.qa.sparkedu.com/user_language/"
    # url = "https://api.qa.sparkedu.com/third/student"
    url = "https://api.qa.sparkedu.com/user/appoint/add"
    phone = '13716640630'
    # post_data = {"avatar":"https://stalegacy.huohua.cn/image/huohua/avatar/default/default_avatar1.png",
    #              "birthday":"2021-01-03 00:00:00",
    #              "id":21434807,
    #              "nickname":"hute222",
    #              "sex":0}
    post_data = {"subjectType": 1}
    # resp = obj_runner.call_rest_api(API_URL=url, phone=phone, req_type="POST", json=post_data)
    resp = obj_runner.week
    print(resp)
