/*
 * @Description: 数据中心
 * @Version: 1.0
 * @Autor: lishengyin
 * @Date: 2022-04-02 13:58:50
 * @LastEditors: lishengyin
 * @LastEditTime: 2022-06-27 11:51:29
 */
#include "GetCenter.hpp"
#include "Disposition.hpp"
#include "Sectorpower.hpp"
#include <vector>
#include <httplib.h>
using namespace httplib;

namespace ins{

    static std::shared_ptr<GetCenter> m_GetCenter = nullptr;

    /**
     * @description: 初始化
     * @param {*}
     * @return {*}
     */    
    int8_t GetCenter::Init() {
        if(this->valid) return OK;
        Disposition::Ptr disposition = Disposition::getPtr();
        #if defined(SUPPORT_DYNAMIC_TEMPLATE)
            //初始化数据
            SqlPool::Instance().Init(disposition->mysql_IP,disposition->mysql_port,"","root",""/*,character*/);
        #else
            //由于需要编译器对可变参数模板的支持，所以gcc5.0以下一般都不支持，否则编译报错
            ErrorL << "your compiler does not support variable parameter templates!" << endl;
            return ERR;
        #endif //defined(SUPPORT_DYNAMIC_TEMPLATE)
        // 初始化数据库连接池
        SqlPool::Instance().setSize(2 + thread::hardware_concurrency());
        this->valid = true;
        this->timer = std::make_shared<Timer>(1.0f, [&](){
            if(!Sectorpower::getPtr()->getEnable()) return true;
            if(!Disposition::getPtr()->getKsToken()) this->getKsToken();
            if(!this->getData()) ErrorL << "Data acquisition failure" << endl;
            return true;
        },nullptr);
        return OK;
    }

    /**
     * @description: 释放
     * @param {*}
     * @return {*}
     */    
    void GetCenter::Destroy() {
        return;
    }

    /**
     * @description: 创建新的实例
     * @param {*}
     * @return {*}
     */    
    std::shared_ptr<GetCenter> GetCenter::CreateNew(){
        if(m_GetCenter == nullptr) m_GetCenter = std::shared_ptr<GetCenter>(new GetCenter);
        return m_GetCenter;
    }
    
    /**
     * @description: 获取ptr
     * @param {*}
     * @return {*}
     */    
    std::shared_ptr<GetCenter> GetCenter::getPtr(){
        return m_GetCenter;
    }
    
    /**
     * @description: 获取ks token
     * @param {*}
     * @return {*}
     */    
    bool GetCenter::getKsToken(){
        Disposition::Ptr disposition = Disposition::getPtr();
        httplib::Client cli(disposition->k8s_IP, disposition->k8s_port);
        httplib::Params params;
        params.emplace("grant_type", "password");
        params.emplace("username", "admin");
        params.emplace("client_id", "kubesphere");
        params.emplace("client_secret", "kubesphere");
        params.emplace("password", "Sunwin2021");
        if (auto res = cli.Post("/oauth/token", params)){
            if(res->status != 200) {
                ErrorL << res->body << endl;
                return false;
            }
            KubusphrereToken token;
            if(token.jsonToObject(res->body)) disposition->KsToken = token;
            InsUniversalData::Ptr data = std::make_shared<InsUniversalData>();
            data->format = InsDataFormat::INS_DATA_FORMAT_JSON;
            data->data = res->body;
            std::shared_ptr<InsPacket> packet = InsPacket::CreateNew(ins::InsPacketTypes::INS_K8SSERVER_PACKET, ins::InsPacketTypes::INS_MYSQL_PACKET, data);
        }else{
            ErrorL << "service is not available" << endl;
        }
        return true;  
    }

    /**
     * @description: 获取包类型
     * @param {string} type
     * @return {*}
     */    
    InsPacketTypes GetCenter::getPacketType(std::string type){
        if(type == "apollo") return InsPacketTypes::INS_APOLLO_PACKET;
        else if(type == "kubesphere") return InsPacketTypes::INS_KUBESPHERE_PACKET;
        return InsPacketTypes::INS_ROUTINE_PACKET;
    }

    /**
     * @description: 获取数据类型
     * TODO 未加入解析工程
     * @param {string} type
     * @param {string} URL
     * @return {*}
     */    
    InsDataFormat GetCenter::getFormat(std::string type, std::string URL){
        return InsDataFormat::INS_DATA_FORMAT_JSON;
    }
    
    /**
     * @description: 获取数据
     * @param {*}
     * @return {*}
     */    
    bool GetCenter::getData(){
        Disposition::Ptr disposition = Disposition::getPtr();
        // 查询数据源
        SqlWriter SqlSelect("SELECT URL,TYPE,DESCRIPTIONID FROM InsServerDB.`InsDataSourceList` WHERE USERID = 2 AND ISDELETE = 0");
        SqlSelect << disposition->getUserId();
        vector<vector<std::string>> result;
        // 查询
        SqlSelect << result;
        if(result.empty()) return false;
        for(auto &line: result){
            std::shared_ptr<httplib::Client> clinet = nullptr;
            std::string type = line[1];
            if(type == "apollo") clinet = std::make_shared<httplib::Client>(disposition->apollo_IP, disposition->apollo_port);
            else if(type == "kubesphere") clinet = std::make_shared<httplib::Client>(disposition->k8s_IP, disposition->k8s_port);
            if(clinet == nullptr || line[0] == "") return false;
            SqlStream sqlStream(line[0].c_str());
            if(type == "kubesphere") sqlStream << Disposition::getPtr()->getWorkspace();
            std::string request = (std::string)sqlStream;
            httplib::Headers headers;
            // token认证
            if(type == "kubesphere")headers.emplace("Authorization", disposition->KsToken.token_type + " " + disposition->KsToken.access_token);
            if(auto res = clinet->Get(request.c_str(), headers)){
                if(res->status != 200){
                    ErrorL << "Request failed, status:" << res->status << ", body:" << res->body << endl;
                    continue;
                }
                InsUniversalData::Ptr data = std::make_shared<InsUniversalData>();
                data->data = res->body;
                data->format = this->getFormat(type, line[0]);
                data->attr1 = line[2];
                InsPacket::Ptr packet = InsPacket::CreateNew(getPacketType(type),InsPacketTypes::INS_MYSQL_PACKET,data);
                if(TaskCenter::getPtr()->ProcessingPacket(packet) != OK) {
                    WarnL << "Build an analytical loss loss, request:" << request.c_str() << endl;
                    continue;
                }
            }else {
                ErrorL << "service is not available:" << request.c_str() << endl;
                continue;
            }
        }
        return true;
    }
    
    /**
     * @description: 执行sqlz
     * @param {string&} sql
     * @return {vector<vector<std::string>>} result
     */    
    vector<vector<std::string>> GetCenter::ExecuteSql(std::string& sql){
        vector<vector<std::string>> result;
        if(sql == "") return result;
        SqlWriter sqlExecter(sql.c_str());
        sqlExecter << result;
        return result;
    }
};