#include "stdafx.h"
#include "CNetWork.h"
#include <nlohmann/json.hpp>

#include <fmt/format.h>
#include "Logger.h"

CNetWork::CNetWork() : m_http_ret(HTTPRET::http_f)
{
	initSessionPool(100);
}

CNetWork::~CNetWork(void)
{
	DestoryOneConnSessionPool();
}

int  CNetWork::GetHttpStatus() {
	return m_http_status;
}

void CNetWork::SetUrlArray(std::vector<ServerListUrl> data) {
	m_url_list = data;
}

HTTPRET  CNetWork::Version(std::string& data) {
	std::vector<cpr::Parameter> p;
	p.push_back({ "tag","win" });
	p.push_back({ "appverion",S_CW2A(VERSION).GetBuffer(0)});
	std::string text = GetUrl("/api/client/v3/version", p, CApp::getSingletonPtr()->GetUserinfo()->access_token);
	if (text.empty()) {

		return HTTPRET::http_f;
	}
	data = text.c_str();
	return HTTPRET::http_yes;
}

HTTPRET CNetWork::Refresh(std::string& data) {
	std::vector<cpr::Parameter> p;
	std::string text = GetUrl("/api/client/v3/refresh", p, CApp::getSingletonPtr()->GetUserinfo()->access_token);
	if (text.empty()) {

		return HTTPRET::http_f;
	}
	data = text.c_str();
	return HTTPRET::http_yes;
}


HTTPRET CNetWork::Auth(std::string& data){
	std::vector<cpr::Parameter> p;
	std::string text = GetUrl("/api/client/v3/authUser",p,CApp::getSingletonPtr()->GetUserinfo()->access_token);
	if (text.empty()) {
		return HTTPRET::http_f;
	}
	data = text.c_str();
	return HTTPRET::http_yes;
}
HTTPRET CNetWork::GetServerNode(std::string& data) {
	std::vector<cpr::Parameter> p;
	std::string text = GetUrl("/api/client/v4/nodes",p,CApp::getSingletonPtr()->GetUserinfo()->access_token);
	if (text.empty()) {

		return HTTPRET::http_f;
	}

	data = text.c_str();


	return HTTPRET::http_yes;
}


HTTPRET CNetWork::GetSysConfig(std::string& data)
{
	AutoToggleNode();
	std::vector<cpr::Parameter> p;
	std::string text = GetUrl("/api/client/v3/getconfig", p);
	if (text.empty()) {
		if (m_http_status == 445)
		{
			return HTTPRET::http_user_expired_at;
		}
		else if (m_http_status == 446)
		{
			return HTTPRET::http_user_transfer_enable;
		}
		else {
			return HTTPRET::http_f;
		}

	}

	data = text.c_str();

	return HTTPRET::http_yes;
}

HTTPRET CNetWork::PostReg(LPCSTR username, LPCSTR password, std::string& data) {
	std::vector<cpr::Pair> p;
	p.push_back({ "username",""});
	p.push_back({ "email",username });
	p.push_back({ "password",password });
	std::string text = PostUrl("/api/client/v2/register", p);
	if (text.empty()) {
		if (m_http_status == 445)
		{
			return HTTPRET::http_user_expired_at;
		}
		else if (m_http_status == 446)
		{
			return HTTPRET::http_user_transfer_enable;
		}
		else {
			return HTTPRET::http_f;
		}

	}

	data = text.c_str();

	return HTTPRET::http_yes;
}

HTTPRET CNetWork::PostLogin(LPCSTR username, LPCSTR password, std::string& data)
{
	std::vector<cpr::Pair> p;
	p.push_back({ "email",username });
	p.push_back({ "password",password });
	std::string text = PostUrl("/api/client/v3/login", p);
	if (text.empty()) {
		if (m_http_status == 445)
		{
			return HTTPRET::http_user_expired_at;
		} else if (m_http_status == 446)
		{
			return HTTPRET::http_user_transfer_enable;
		}
		else {
			return HTTPRET::http_f;
		}
		
	}

	data = text.c_str();

    return HTTPRET::http_yes;
}

HTTPRET  CNetWork::GetSysConfigFromUser(LPCSTR username, LPCSTR password, std::string& data) {

	 

	std::vector<cpr::Parameter> p;
	p.push_back({ "email",username });
	p.push_back({ "password",password });
	std::string text = GetUrl("/api/client/v3/getconfig", p);
	if (text.empty()) {
		if (m_http_status == 445)
		{
			return HTTPRET::http_user_expired_at;
		}
		else if (m_http_status == 446)
		{
			return HTTPRET::http_user_transfer_enable;
		}
		else {
			return HTTPRET::http_f;
		}

	}

	data = text.c_str();

	return HTTPRET::http_yes;
}

void CNetWork::SetUrl(LPCSTR url)
{
	m_url = url;
}

void CNetWork::initSessionPool(int szie) {


	for (int i = 0 ; i < szie ; i++)
	{
		std::shared_ptr<cpr::Session> pp = std::make_shared<cpr::Session>();
		m_session_vect.push_back(std::move(pp));
	}
	
}

void CNetWork::DestoryOneConnSessionPool() {
	for (auto &conn : m_session_vect)
	{
		std::move(m_session_vect.front());
	}
}

std::shared_ptr<cpr::Session> CNetWork::GetSession() {
	std::shared_ptr<cpr::Session> pp = nullptr;
	m_mutx.lock();
	if (m_session_vect.size() > 0)
	{
		pp = m_session_vect.front();
		m_session_vect.pop_back();
	}
	m_mutx.unlock();
	/*if (pp == nullptr)
	{
		pp = std::make_shared<cpr::Session>();
	}*/

	return pp;
}

void CNetWork::pullSession(std::shared_ptr<cpr::Session> &p) {
	m_mutx.lock();
	m_session_vect.push_back(p);
	m_mutx.unlock();
}

SStringA CNetWork::GetLastErrorA()
{
	return SStringA().Format("%s", m_error_msg.c_str()).GetBuffer(0);
}

SStringW CNetWork::GetLastErrorW()
{
	return S_CA2W(m_error_msg.c_str(),CP_UTF8).GetBuffer(0);
}

std::string CNetWork::GetUrl(std::string path, std::vector<cpr::Parameter> parame,std::string token)
{
	

	cpr::Parameters ps;
	std::vector<cpr::Parameter>::iterator it_i;
	for (it_i = parame.begin(); it_i != parame.end(); ++it_i)
	{
		ps.Add(*it_i);
		//ps(cpr::Parameter({ it->first, it->second }));
	}

	cpr::Header hander;

	if (!token.empty())
	{
		hander = cpr::Header{ {"accept", "application/json"} , {"Authorization", "bearer " + token} };
	}
	else {
		hander = cpr::Header{ {"accept", "application/json"} };
	}

	

	cpr::Response r;


	std::string res_test = "";
	//auto session = GetSession();
	int count = 3;
	/*int serverurlid = 0;*/
	do
	{
		if (count <= 0)
		{
			break;
		}

		auto s = fmt::format("{0}{1}", m_url, path.c_str());
		
		if (parame.empty())
		{
			m_session.SetHeader(hander);
			m_session.SetVerifySsl(false);
			m_session.SetTimeout(cpr::Timeout{TIMEOUTE});
			m_session.SetUrl(s.c_str());
			//session->SetDebugCallback()
			r = m_session.Get();
			//r = cpr::Get(cpr::Url{ s.c_str() }, cpr::VerifySsl{ false }, hander, cpr::Timeout{ 60 * 1000 });
		}
		else {
			m_session.SetHeader(hander);
			m_session.SetVerifySsl(false);
			m_session.SetTimeout(cpr::Timeout{ TIMEOUTE });
			m_session.SetUrl(s.c_str());
			m_session.SetParameters(ps);
			r = m_session.Get();
			//r = cpr::Get(cpr::Url{ s.c_str() }, cpr::VerifySsl{ false }, ps, hander, cpr::Timeout{ 60 * 1000 });
		}
		if (r.status_code == 200 || r.status_code == 201)
		{
			res_test = r.text;
			break;
		}
		else {

			m_http_status = r.status_code;
			if (r.error.message.empty())
			{
				m_error_msg = r.status_line;
			}
			else {
				m_error_msg = UpdateError(r.error.code, r.error.message);
			}

		}

		if (!res_test.empty())
		{
			break;
		}
		Sleep(3000);
		count--;
		Logger::getSingletonPtr()->INFO("get 重试" + std::to_string(count) + "次数");

	} while (res_test.empty());

	//pullSession(session);
	
	return res_test;
}

std::string CNetWork::PostUrl(std::string path, std::vector<cpr::Pair> parame, std::string token)
{
	auto s = fmt::format("{0}{1}", m_url, path.c_str());

	cpr::Header hander;

	if (!token.empty())
	{
		hander = cpr::Header{ {"accept", "application/json"} , {"Authorization", "bearer " + token} };
	}
	else {
		hander = cpr::Header{ {"accept", "application/json"} };
	}

	cpr::Response r;


	std::string res_test = "";
	/**
 * \brief 
 * \param  
 * \param  
 * \param  
 * \return 
 */


	int count = 3;
	do
	{
		if (count <= 0)
		{
			break;
		}
		
		if (parame.empty())
		{
			//r = cpr::Get(cpr::Url{ s.c_str() }, hander, cpr::VerifySsl{false}, cpr::Timeout{ 60 * 1000 });
			m_session.SetHeader(hander);
			m_session.SetVerifySsl(false);
			m_session.SetTimeout(cpr::Timeout{ TIMEOUTE });
			m_session.SetUrl(s.c_str());
			r = m_session.Get();
		}
		else {
			m_session.SetHeader(hander);
			m_session.SetVerifySsl(false);
			m_session.SetTimeout(cpr::Timeout{ TIMEOUTE });
			m_session.SetUrl(s.c_str());
			m_session.SetPayload(cpr::Payload{ parame.begin(),parame.end() });
			r = m_session.Post();
			//r = cpr::Post(cpr::Url{ s.c_str() }, cpr::VerifySsl{ false }, cpr::Payload{ parame.begin(),parame.end() }, hander, cpr::Timeout{ 60 * 1000 });
		}
		//Logger::getSingletonPtr()->DEBUG(r.url.c_str());
		if (r.status_code == 200 || r.status_code == 201)
		{
			res_test = std::move(r.text);
			break;
		}
		else {

			m_http_status = r.status_code;
			if (r.error.message.empty())
			{
				m_error_msg = r.status_line;
			}
			else {

				m_error_msg = UpdateError(r.error.code, r.error.message);
			}
 
		}

		if (!res_test.empty())
		{
			break;
		}

		Logger::getSingletonPtr()->INFO("重试" + std::to_string(count) + "次数");
		count--;


	} while (res_test.empty());
	//pullSession(session);

	return res_test;
}
void CNetWork::AutoToggleNode() {

	cpr::Header hander;
	hander = cpr::Header{ {"accept", "application/json"} };
	 
	std::vector<cpr::Parameter> p;
	p.push_back({ "tag","win" });
	p.push_back({ "appverion",S_CW2A(VERSION).GetBuffer(0) });
	cpr::Parameters ps;
	std::vector<cpr::Parameter>::iterator it_i;
	for (it_i = p.begin(); it_i != p.end(); ++it_i)
	{
		ps.Add(*it_i);
		//ps(cpr::Parameter({ it->first, it->second }));
	}
	//m_url_list.reserve(m_url.size());

	//切换节点，
	for each (ServerListUrl  itme in m_url_list)
	{
		cpr::Session m_session;
		auto s = fmt::format("{0}{1}", itme.url, "/api/client/v3/version");
		m_session.SetHeader(hander);
		m_session.SetVerifySsl(false);
		m_session.SetTimeout(cpr::Timeout{ TIMEOUTE });
		m_session.SetUrl(s.c_str());
		m_session.SetParameters(ps);
		auto r = m_session.Get();
		if (r.status_code != 200) {
			continue;
		}
		else
		{
			if (m_url != itme.url)
			{
				m_url = itme.url;
				Logger::getSingletonPtr()->INFO(S_CW2A(itme.name).GetBuffer(0));
			}
			break;
		}
	}



 
}
std::string CNetWork::Retrying(std::string path, std::vector<cpr::Parameter> parame, std::string token)
{

	std::string res_test = "";

	int count = 3;
	do 
	{
		if (count <= 0)
		{
			break;
		}

		res_test = GetUrl(path, parame, token);

		if (!res_test.empty())
		{
			break;
		}
		count--;


	} while (res_test.empty());
	

	return res_test;
}

bool  write_data(std::string /*data*/, intptr_t /*userdata*/)
{

	return true;
}

bool CNetWork::Download(std::string path)
{
	return false;
	/*std::string configdir = std::filesystem::current_path().string() + "\\" + CLASHCONFIGDIR;

	std::filesystem::path p(configdir);

	if (!std::filesystem::exists(p)) {
		std::filesystem::create_directory(p);
	}

	std::string configname = configdir + "\\" CLASHCONFIGNAME;

	std::ofstream ofs(configname);
	cpr::Url url{ path };
	cpr::Session session;
	session.SetUrl(url);
	cpr::Response response = session.Download(ofs);
	return response.status_code == 200;*/
}


void CNetWork::Init()
{

}

void CNetWork::UnInit()
{
}

std::string CNetWork::UpdateError(cpr::ErrorCode code, std::string msg)
{
	if (code == cpr::ErrorCode::HOST_RESOLUTION_FAILURE)
	{
		m_error_msg = "解析域名失败";
	}
	else if (code == cpr::ErrorCode::OPERATION_TIMEDOUT)
	{
		m_error_msg = "请求数据超时,请重新请求";
	}
	else if (code == cpr::ErrorCode::CONNECTION_FAILURE) {
		m_error_msg = "发送服务器失败";
	}
	else if (code == cpr::ErrorCode::INVALID_URL_FORMAT) {
		m_error_msg = "未知URL错误";

	} else if (code == cpr::ErrorCode::SSL_CONNECT_ERROR)
	{
		m_error_msg = "SSL连接错误";
	}
	else {
		m_error_msg = msg;
	}

	return m_error_msg;
}
