/**
 * AUSF API
 * AUSF UE Authentication Service. © 2020, 3GPP Organizational Partners (ARIB,
 * ATIS, CCSA, ETSI, TSDSI, TTA, TTC). All rights reserved.
 *
 * The version of the OpenAPI document: 1.1.1
 *
 *
 * NOTE: This class is auto generated by OpenAPI Generator
 * (https://openapi-generator.tech). https://openapi-generator.tech Do not edit
 * the class manually.
 */

#include "DefaultApiImpl.h"

#include "OCTET_STRING.h"
#include "authentication_algorithms_with_5gaka.hpp"
#include "comUt.hpp"
#include "logger.hpp"
#include <iostream>

#include "conversions.hpp"
#include "sha256.hpp"
#include "UEAuthenticationCtx.h"
#include "ConfirmationDataResponse.h"
#include "AuthenticationInfo.h"

#include <typeinfo>
#include <map>
#include "ausf_config.hpp"
#include "curl.hpp"
#include "ProblemDetails.h"

using namespace config;
extern ausf_config ausf_cfg;

using namespace std;

namespace org {
namespace openapitools {
namespace server {
namespace api {

using namespace org::openapitools::server::model;


//stored temporarily
uint8_t XRES_STAR[16];  //store xres*
// uint8_t KAUSF[32];      
std::string SUPI_AUSF;  //store supi
std::map<std::string, std::string> authctxid_supi;

std::string AUTH_TYPE;    //store authType
std::string SERVING_NN;   //store serving network name

std:: string KAUSF_TMP; //store Kausf(string)

typedef struct {
  uint8_t rand[16];
  uint8_t autn[16];
  uint8_t hxresStar[16];
  uint8_t kseaf[32];
  uint8_t XRES_STAR[16];  //store xres*
} AUSF_AV_s;

AUSF_AV_s ausf_av_s;
vector<AUSF_AV_s> all_AVs;

DefaultApiImpl::DefaultApiImpl(std::shared_ptr<Pistache::Rest::Router> rtr)
    : DefaultApi(rtr) {}

void DefaultApiImpl::eap_auth_method(const std::string &authCtxId,
                                     const EapSession &eapSession,
                                     Pistache::Http::ResponseWriter &response) {
  response.send(Pistache::Http::Code::Ok, "Do some magic\n");
}

void DefaultApiImpl::rg_authentications_post(
    const RgAuthenticationInfo &rgAuthenticationInfo,
    Pistache::Http::ResponseWriter &response) {
  response.send(Pistache::Http::Code::Ok, "Do some magic\n");
}

void DefaultApiImpl::ue_authentications_auth_ctx_id5g_aka_confirmation_put(
    const std::string &authCtxId, const ConfirmationData &confirmationData,
    Pistache::Http::ResponseWriter &response) {
  // seaf --> ausf
  ProblemDetails problemDetails;
  nlohmann::json problemDetails_json = {};
  Logger::ausf_server().debug("Handling 5g-aka-confirmation-put...");

  if(authctxid_supi.count(authCtxId) <= 0) //no ue-authentications request ever
  {
    problemDetails.setCause("SERVING_NETWORK_NOT_AUTHORIZED");
    problemDetails.setStatus(403);
    problemDetails.setDetail("Serving Network Not Authorized");
    to_json(problemDetails_json,problemDetails);

    Logger::ausf_server().error("Serving Network Not Authorized");
    Logger::ausf_server().info("Send 403 Forbidden response to AUSF");
    response.send(Pistache::Http::Code::Forbidden,  problemDetails_json.dump());
    return;
  }


  /*---------------------getting params---------------------------------*/
  // Logger::ausf_server().info("Received authCtxId %s", authCtxId.c_str());    // authCtxId
  // Logger::ausf_server().info("Received res* %s", confirmationData.getResStar().c_str());    // res*(const char*)  "0ddc2f64bd8dc35906fad44499e18525"

  uint8_t resStar[16] = {0}; 
  conv::hex_str_to_uint8(confirmationData.getResStar().c_str(), resStar); //string->uint8, res*(uint8)    
  
  /*---------------------认证确认-amf--------------------------------*/ 
  ConfirmationDataResponse confirmResponse;
  //1.验证AV是否过期: 认证成功后，AUSF将存储 KAUSF  ?
  uint8_t authCtxId_seaf[16];
  conv::hex_str_to_uint8(authCtxId.c_str(), authCtxId_seaf); //authCtxId in seaf

  int ausf_av_index = 0;
  for(int i=0;i<all_AVs.size();i++){
    // Logger::ausf_server().debug("authCtxId in ausf: %s", (conv::uint8_to_hex_string(all_AVs[i].autn, 16)).c_str());
    if(Authentication_5gaka::equal_uint8(all_AVs[i].autn,authCtxId_seaf,16)){
      ausf_av_index = i;
      break;
    }
  }
  if(ausf_av_index == all_AVs.size()){
    Logger::ausf_server().error("Authentication failure by home network with authCtxId %s: AV expired",authCtxId.c_str());
    confirmResponse.setAuthResult(false);  //向SEAF指示，从本地网络角度认证not成功
    KAUSF_TMP = "invalid";
  }
  else  //AV valid
  {
    Logger::ausf_server().info("AV is up to date, handling received res*...");

    //store Kausf

    //2.将接收到的RES*与存储的XRES*进行比较:RES*与XRES*相等，AUSF将从家庭网络角度认为认证成功
    //get stored xres* -----
    uint8_t xresStar[16] = {0}; 
    memcpy(xresStar,all_AVs[ausf_av_index].XRES_STAR,16);  //xres* stored for 5g-aka-confirmation
    // Logger::ausf_server().debug("xres* in ausf: %s", (conv::uint8_to_hex_string(xresStar, 16)).c_str());
    // Logger::ausf_server().debug("xres in amf: %s", (conv::uint8_to_hex_string(resStar, 16)).c_str());

    bool authResult = Authentication_5gaka::equal_uint8(xresStar,resStar,16);
    confirmResponse.setAuthResult(authResult);  //3.向SEAF指示，从本地网络角度认证是否成功

    if(!authResult) //fail
    {
      Logger::ausf_server().error("Authentication failure by home network with authCtxId %s: res* != xres*",authCtxId.c_str());
    }
    else  //success
    {
      Logger::ausf_server().info("Authentication successful by home network!");
      
      //4.send KSEAF to SEAF
      string kseaf_s;
      kseaf_s = conv::uint8_to_hex_string(all_AVs[ausf_av_index].kseaf, 32);  // convert uint8_t to string
      confirmResponse.setKseaf(kseaf_s); 

      //5.send supi when supi_ausf exists
      if(authctxid_supi.count(authCtxId) > 0)
      {
        confirmResponse.setSupi(authctxid_supi.at(authCtxId));
        authctxid_supi.erase(authCtxId);
      }

      //6. send authResult to udm
      //UDM POST interface ----- send authentication result info--------------------
      std::string udm_ip = std::string(inet_ntoa (*((struct in_addr *)&ausf_cfg.nudm.addr4)));  //need to change to nudm
      std::string udm_port = std::to_string(ausf_cfg.nudm.port);
      std::string udmUri;
      std::string Method;
      std::string Response;

      udmUri = "http://" + udm_ip+ ":" + udm_port + "/nudm-ueau/v1/" + confirmResponse.getSupi() + "/auth-events";
      cout << udmUri.c_str() << endl;
      Logger::ausf_server().debug("Send POST Request URI: " + udmUri);
      Method = "POST"; 

      //form udm request body 
      nlohmann::json confirmResultInfo = {};
      confirmResultInfo["nfInstanceId"] = "400346f4-087e-40b1-a4cd-00566953999d";    //fixed, may need to change
      confirmResultInfo["success"] = true;  

      time_t rawtime;
      time(&rawtime);
      char buf[32];
      strftime(buf, sizeof(buf), "%FT%TZ", gmtime(&rawtime));
      confirmResultInfo["timeStamp"] = buf;  //timestamp generated
      // confirmResultInfo["timeStamp"] = "2020-08-12T17:02:51.128672225+09:00";  //PARAM

      confirmResultInfo["authType"] = AUTH_TYPE;  //authType stored in ausf
      confirmResultInfo["servingNetworkName"] = SERVING_NN;  //snn stored in ausf
      confirmResultInfo["authRemovalInd"] = false;  

      cout << confirmResultInfo.dump() << endl;
      Curl::curl_http_client(udmUri,  Method,  confirmResultInfo.dump() , Response);

    }
  }

  /*----------------ausf --> seaf-----------*/
  //---forming response
  nlohmann::json confirmResponse_json;
  to_json(confirmResponse_json, confirmResponse);
  // Logger::ausf_server().debug("5g-aka-confirmation response:\n %s",confirmResponse_json.dump().c_str());

  Logger::ausf_server().info("Send 5g-aka-confirmation 200 response to SEAF");
  

  response.send(Pistache::Http::Code::Ok, confirmResponse_json.dump());
}

void DefaultApiImpl::ue_authentications_deregister_post(
    const DeregistrationInfo &deregistrationInfo,
    Pistache::Http::ResponseWriter &response) {
  response.send(Pistache::Http::Code::Ok, "Do some magic\n");
}

void DefaultApiImpl::ue_authentications_post(
    const AuthenticationInfo &authenticationInfo,
    Pistache::Http::ResponseWriter &response) {

    // Logger::ausf_server().debug("--ue_authentications_post--");

   /*----------------------getting params-------------*/
    // Logger::ausf_server().info("servingNetworkName %s", authenticationInfo.getServingNetworkName().c_str());
    Logger::ausf_server().info("supiOrSuci %s", authenticationInfo.getSupiOrSuci().c_str());

    std::string snn = authenticationInfo.getServingNetworkName(); //serving network name
    std::string supi = authenticationInfo.getSupiOrSuci();        //supi

    /* -----------5g he av from udm-----------------*/

    //UDM GET interface ----- get authentication related info--------------------
    std::string udm_ip = std::string(inet_ntoa (*((struct in_addr *)&ausf_cfg.nudm.addr4)));  //need to change to nudm
    std::string udm_port = std::to_string(ausf_cfg.nudm.port);
    std::string udmUri;
    std::string Method;
    std::string Response;

    //UDM POST interface ----- get authentication related info--------------------
    udmUri = "http://" + udm_ip+ ":" + udm_port + "/nudm-ueau/v1/" + supi + "/security-information/generate-auth-data";
    Logger::ausf_server().debug("Send POST Request URI: " + udmUri);
    Method = "POST"; 

    //form udm request body AuthInfo
    nlohmann::json AuthInfo = {};   //model AuthenticationInfo do not have ausfInstanceId field
    AuthInfo["servingNetworkName"] = snn;
    AuthInfo["ausfInstanceId"] = "400346f4-087e-40b1-a4cd-00566953999d";    //fixed, may need to change

    if(authenticationInfo.resynchronizationInfoIsSet()) //set ResynchronizationInfo
    {
      ResynchronizationInfo resynInfo = authenticationInfo.getResynchronizationInfo();

      AuthInfo["resynchronizationInfo"]["rand"] = resynInfo.getRand();
      AuthInfo["resynchronizationInfo"]["auts"] = resynInfo.getAuts();
      Logger::ausf_server().info("received authInfo from amf with ResynchronizationInfo");
    }
    else
    {
      Logger::ausf_server().info("received normal authInfo from amf");
    }

    Curl::curl_http_client(udmUri,  Method,  AuthInfo.dump() , Response);

    Logger::ausf_server().debug("Recevied POST response body: %s",Response.c_str());
    
    ProblemDetails problemDetails;
    nlohmann::json problemDetails_json = {};

    nlohmann::json response_data = {};
    try
      {
        response_data = nlohmann::json::parse(Response.c_str());
      }
    catch (nlohmann::json::exception &e)
      {
        Logger::ausf_server().info("Could not get Json content from UDM response");

        //TODO: error handling
        problemDetails.setCause("CONTEXT_NOT_FOUND");
        problemDetails.setStatus(404);
        problemDetails.setDetail("Resource corresponding to User " + supi + " not found in UDM");
        to_json(problemDetails_json,problemDetails);

        Logger::ausf_server().error("Resource corresponding to User " + supi + " not found in UDM");
        Logger::ausf_server().info("Send 404 Not_Found response to AUSF");
        response.send(Pistache::Http::Code::Not_Found,  problemDetails_json.dump());
        return;
      }

    std::string authType_udm = response_data.at("authType");                //AuthType
    // Logger::ausf_server().debug("authType %s",authType_udm.c_str());
    std::string autn_udm = response_data["authenticationVector"].at("autn");  //autn
    // Logger::ausf_server().debug("autn_udm %s",autn_udm.c_str());
    std::string avType_udm = response_data["authenticationVector"].at("avType");//avType
    // Logger::ausf_server().debug("avType_udm %s",avType_udm.c_str());
    std::string kausf_udm = response_data["authenticationVector"].at("kausf");  //kausf
    // Logger::ausf_server().debug("kausf_udm %s",kausf_udm.c_str());
    std::string rand_udm = response_data["authenticationVector"].at("rand");    //rand
    // Logger::ausf_server().debug("rand_udm %s",rand_udm.c_str());
    std::string xresStar_udm = response_data["authenticationVector"].at("xresStar");  //xres*
    // Logger::ausf_server().debug("xres*_udm %s",xresStar_udm.c_str());
    
    //------------------5G HE AV-----------------------------------------------------
    uint8_t autn[16] = {0};
    uint8_t rand[16] = {0};
    uint8_t xresStar[16] = {0};
    uint8_t kausf[32] = {0};
    
    conv::hex_str_to_uint8(autn_udm.c_str(),autn);    //autn
    conv::hex_str_to_uint8(rand_udm.c_str(),rand);    //rand
    conv::hex_str_to_uint8(xresStar_udm.c_str(),xresStar);  //xres*
    conv::hex_str_to_uint8(kausf_udm.c_str(),kausf);    //kausf

    /*----------------------generating 5G AV from 5G HE AV--------------------------*/
    /*  HXRES* <-- XRES*  */
    /*  KSEAF  <-- KAUSF  */
    /*  在5G HE AV中用HXRES*替代XRES*，用 KSEAF 替代KAUSF */
    /*  删除 KSEAF，并向SEAF返回5G SE AV（RAND, AUTN, HXRES*）*/
    /*  A.5, 3gpp ts33.501 */ 
    // Logger::ausf_server().debug("==generating 5g av");

    //--------generating hxres*
    
    uint8_t rand_ausf[16] = {0};
    uint8_t autn_ausf[16] = {0};
    uint8_t xresStar_ausf[16] = {0};
    uint8_t kausf_ausf[32] = {0};
    uint8_t hxresStar[16] = {0};
    
    //getting params from udm 5G HE AV------may be simplified
    memcpy(xresStar_ausf,xresStar,16);  //xres*
    memcpy(rand_ausf,rand,16);          //rand
    memcpy(autn_ausf,autn,16);          //autn
    memcpy(kausf_ausf,kausf,32);          //kausf

    //generate_Hxres*
    Authentication_5gaka::generate_Hxres(rand_ausf,xresStar_ausf,hxresStar);
    // Logger::ausf_server().debug("hxresStar calculated:\n %s", (conv::uint8_to_hex_string(hxresStar, 16)).c_str());

    uint8_t kseaf[32] = {0};
    Authentication_5gaka::derive_kseaf(snn,kausf,kseaf);
    // Logger::ausf_server().debug("kseaf calculated:\n %s", (conv::uint8_to_hex_string(kseaf, 32)).c_str());

    memcpy(ausf_av_s.rand,rand_ausf,16);  //store 5g av in ausf
    memcpy(ausf_av_s.autn,autn_ausf,16); 
    memcpy(ausf_av_s.hxresStar,hxresStar,16); 
    memcpy(ausf_av_s.kseaf,kseaf,32);
    memcpy(ausf_av_s.XRES_STAR,xresStar,16);        //store xres* in ausf
    all_AVs.push_back(ausf_av_s);
    // SUPI_AUSF = authenticationInfo.getSupiOrSuci(); //store supi in ausf
    SERVING_NN = snn;                        //store snn in ausf
    AUTH_TYPE = authType_udm;                 //store authType in ausf
    KAUSF_TMP = conv::uint8_to_hex_string(kausf_ausf,32); //store kausf_tmp in ausf

    /*----------------ausf --> seaf-----------*/
    //---form UEAuthenticationCtx
    UEAuthenticationCtx UEAuthCtx;

    string rand_s = conv::uint8_to_hex_string(rand_ausf, 16);
    string autn_s = conv::uint8_to_hex_string(autn_ausf, 16);
    string hxresStar_s = conv::uint8_to_hex_string(hxresStar, 16);

    UEAuthCtx.setAuthType(authType_udm);//authType(string)

    //links(std::map)
    std::map<std::string, LinksValueSchema> ausf_links;
    LinksValueSchema ausf_Href;
    std::string resourceURI;

    std::string authCtxId_s;
    authCtxId_s = autn_s;   // authCtxId = autn

    authctxid_supi[authCtxId_s] = authenticationInfo.getSupiOrSuci(); //store supi in ausf

    std::string ausf_ip = std::string(inet_ntoa (*((struct in_addr *)&ausf_cfg.sbi.addr4)));
    std::string ausf_port = std::to_string(ausf_cfg.sbi.port);

    resourceURI = "http://" + ausf_ip + ":" + ausf_port + "/nausf-auth/v1/ue-authentications/" + authCtxId_s + "/5g-aka-confirmation";
    ausf_Href.setHref(resourceURI);  //"/nausf-auth/v1/ue-authentications/640110987654321/5g-aka-confirmation"

    std::string Location = resourceURI;

    // cout << ausf_Href.getHref().c_str() << endl;
    ausf_links["5G_AKA"] = ausf_Href;
    UEAuthCtx.setLinks(ausf_links);

    //----------5gAuthData(Av5gAka):rand autn hxresStar
    Av5gAka ausf_5gAuthData;
    ausf_5gAuthData.setRand(rand_s);
    ausf_5gAuthData.setAutn(autn_s);
    ausf_5gAuthData.setHxresStar(hxresStar_s);
    UEAuthCtx.setR5gAuthData(ausf_5gAuthData);

    nlohmann::json UEAuthCtx_json;
    to_json(UEAuthCtx_json,UEAuthCtx);
    // Logger::ausf_server().debug("auth response:\n %s",UEAuthCtx_json.dump().c_str());

    Logger::ausf_server().info("Send 201 response to SEAF");
    response.headers().add<Pistache::Http::Header::Location>(Location);
    response.send(Pistache::Http::Code::Created, UEAuthCtx_json.dump());  //Type: json object to string

}

} // namespace api
} // namespace server
} // namespace openapitools
} // namespace org
