/*
    Tucnak - SOTA SSO and Network API
    Copyright (C) 2002-2020  Ladislav Vaiz <ok1zia@nagano.cz>;
    2020 Michal OK2MUF

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    version 2 as published by the Free Software Foundation.

*/

#include "bfu.h"
#include "qsodb.h"
#include "fifo.h"
#include "stdint.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"

//#include <time.h> // time() should come from ztime.h from libzia
#include <libzia.h>
#include <zjson0.h>

#include "main.h"

//#include "sota.h"
#include "sota_spot.h"
#include "sota_api.h"

//#define SOTA_SPOT_GET
//#define SOTA_SPOT_POST

/*
curl \
  -d "client_id=[client id from SOTA MT]" \
  -d "client_secret=[client secret from SOTA MT]" \
  -d "username=[user name]" \
  -d "scope=openid" \
  -d "password=[password]" \
  -d "grant_type=password" \
  "https://sso.sota.org.uk/auth/realms/SOTA/protocol/openid-connect/token"
*/

void sota_api_parse_token(struct zhttp *zh);
static void sota_api_spot_sent(struct zhttp *http);

struct sota_api_data* sota_api_init(void)
{
    struct sota_api_data *sad = g_new0(struct sota_api_data,1);
    if (sad)
    {
        sad->http = zhttp_init();
        sad->http_spot = zhttp_init();
    }
    return sad;
}

void sota_api_free(struct sota_api_data *sad)
{
    if (sad)
    {
        if (sad->http)
            /* free gs? */
            zhttp_free(sad->http);
        if (sad->http_spot)
            /* free gs? */
            zhttp_free(sad->http_spot);
        g_free(sad);
    }
}

int sota_api_get_token(struct sota_api_data *sad)
{
#ifdef SOTA_SPOT_GET
    GString *gs;

    gs = g_string_sized_new(250);

    //zg_string_eprintfa("", gs, "http://192.168.11.181/");
    zg_string_eprintfa("", gs, "http://sota.emuf.cz:8044/tokentest/");
    //zg_string_eprintfa("", gs, "https://sso.sota.org.uk/auth/realms/SOTA/protocol/openid-connect/token");
    zg_string_eprintfa("u", gs, "?client_id=tucnak"); 
    zg_string_eprintfa("u", gs, "&client_secret=" SOTA_CLIENT_SECRET); 
    zg_string_eprintfa("u", gs, "&scope=openid"); 
    zg_string_eprintfa("u", gs, "&grant_type=password"); 
    zg_string_eprintfa("u", gs, "&username=%s", cfg->sota_user); 
    zg_string_eprintfa("u", gs, "&password=%s", cfg->sota_pass); 

    zhttp_get(sad->http, zsel, gs->str, sota_api_parse_token, sad);
    g_string_free(gs, FALSE); // g_free shall be called by zhttp_get (is so?)

#elif defined(SOTA_SPOT_POST)

    zhttp_post_free(sad->http);
    zhttp_post_add(sad->http, "client_id","tucnak");
    zhttp_post_add(sad->http, "client_secret",SOTA_CLIENT_SECRET);
    zhttp_post_add(sad->http, "username",cfg->sota_pass);
    zhttp_post_add(sad->http, "scope","openid");
    zhttp_post_add(sad->http, "password",cfg->sota_user);
    zhttp_post_add(sad->http, "grant_type","password");

    zhttp_add_header(sad->http, g_strdup("Accept"), g_strdup("*/*"));

    //zhttp_post(sad->http, zsel, "http://192.168.11.181",
    zhttp_post(sad->http, zsel, "https://sso.sota.org.uk/auth/realms/SOTA/protocol/openid-connect/token",
                sota_api_parse_token, sad);

#endif

    // todo g_string_free(gs, TRUE);   
    return 0;
}

void sota_api_parse_token(struct zhttp *zh)
{
    struct sota_api_data *sad = (struct sota_api_data*)zh->arg;

    if (zh->errorstr){
        //g_free(slovhf->status);slovhf->status = g_strdup_printf(TRANSLATE("Can't upload score: %s"), http->errorstr);
        log_addf("Network error: %s", zh->errorstr);
        if (zh->status == 307)
        {
            log_addf("Redirected to: %s", zh->response->buf);
        }
        zhttp_free0(sad->http);// sad->http = NULL;
        g_free(sad);     
        //TODO free sad //free_slovhfnet(slovhf); gses->slovhf = NULL;
        return;        
    }
/*
    * access_token
    * expires_in
    - refresh_expires_in
    - refresh_token
    * id_token
    - token_type
    - not-before-policy
    - session_state
    - scope
*/
    sad->error = zjson0_get_str(zh->datastr, -1, "error");
    sad->error_description = zjson0_get_str(zh->datastr, -1, "error_description");

    if (sad->error != NULL)
    {
        log_addf("Server error: %s", sad->error_description);
        sad->state = SOTA_SSO_ERROR;
        zhttp_free0(sad->http);// sad->http = NULL;
        g_free(sad);
        return;
    }

    sad->access_token = zjson0_get_str(zh->datastr, -1, "access_token");
    sad->id_token = zjson0_get_str(zh->datastr, -1, "id_token");
    if (sad->access_token && sad->id_token)
    {
        sad->state = SOTA_SSO_VALID;
        sota_api_send_spot(sad);
    }
    else
    {
        log_addf("SOTA SSO token parse error");
        sad->state = SOTA_SSO_ERROR;
    }
    
    //sad->ts_token_expire = ztime + zjson_get_int(zh->datastr,-1, "expires_in");
    //char *zjson_get_str(const char *str, int len, const char *path);

}

int sota_api_send_spot(struct sota_api_data *sad)
{
    if (!sad || sad->state != SOTA_SSO_VALID)
    {
        log_addf("SOTA SSO token parse error");
        return 1;
    }

    if (!sad->http_spot || !sad->ssd)
    {
        log_addf("Incomplete data");
        return 1;
    }

    GString *auth_name = g_string_new("Authentication");
    GString *auth_value = g_string_new(NULL);
    
    GString *id_token_name = g_string_new("id_token");
    GString *id_token_value = g_string_new(sad->id_token);

    GString *json_data = g_string_new("{");

    g_string_append_printf(auth_value, "Bearer %s", sad->access_token);
    
    // Two headers to your POST, the first, an Authentication: Bearer [access_token] header, 
    // where [access_token] is replaced with the contents of the access_token that was returned 
    // from the authentication step.  
    // Secondly, a header called id_token is needed, that also contains the id_token from the JWT, 
    // like id_token: [id_token contents]

    zhttp_add_header(sad->http_spot, auth_name->str, auth_value->str);
    zhttp_add_header(sad->http_spot, id_token_name->str, id_token_value->str);

    /*
    Used auth RFC6750
    POST /api/spots
    {
        "associationCode": "W5O",
        "summitCode": "OU-013",
        "activatorCallsign": "GM4ZFZ",
        "frequency": "14.0625",
        "mode": "cw",
        "comments": "test-ignore"
    }
    */

    //zjson_object_start(json_data, "");
    zjson0_item_int(json_data, "id", 0);
    zjson0_item_string(json_data, "associationCode", sad->ssd->associationCode);
    zjson0_item_string(json_data, "summitCode", sad->ssd->summitCode);
    zjson0_item_string(json_data, "Callsign", sad->ssd->callsign);
    zjson0_item_string(json_data, "activatorCallsign", sad->ssd->activatorCallsign);
    zjson0_item_string(json_data, "frequency", sad->ssd->frequency);
    zjson0_item_string(json_data, "mode", sad->ssd->mode);
    zjson0_item_string(json_data, "comments", sad->ssd->comment);
    zjson0_item_string(json_data, "highlightColor", "default");
    zjson0_object_end(json_data);
    zjson0_strip(json_data);

    //zhttp_post_json(sad->http_spot, zsel, "http://192.168.11.181", 
    zhttp_post_json(sad->http_spot, zsel, "https://api2.sota.org.uk/api/spots/",
                    json_data->str, sota_api_spot_sent, sad);
    
    g_string_free(json_data, TRUE);

    g_string_free(auth_name, FALSE);
    g_string_free(auth_value, FALSE);

    g_string_free(id_token_name, FALSE);
    g_string_free(id_token_value, FALSE);

    return 0;
}

static void tokenize(GString *gs_inout)
{
    GString *gs = g_string_new(gs_inout->str);
    char *gsp = gs->str;
    int i = 0;

    g_string_append(gs_inout, gsp);

    while (*gsp != '\0')
    {
        gs_inout->str[i++] = 'c'+((*gsp)>>4);
        gs_inout->str[i++] = 'f'+(*gsp & 0xf);
        gsp++;
    }

    g_string_free(gs, TRUE);
}

int sota_api_send_spot_insecure(struct sota_api_data *sad)
{

    GString *gs_token = g_string_new(cfg->sota_pass);

    zhttp_post_add(sad->http_spot, "spot", "add");
    zhttp_post_add(sad->http_spot, "associationCode", sad->ssd->associationCode);
    zhttp_post_add(sad->http_spot, "summitCode", sad->ssd->summitCode);
    //zhttp_post_add(sad->http_spot, "callsign", sad->ssd->callsign);
    zhttp_post_add(sad->http_spot, "activatorCallsign", sad->ssd->activatorCallsign);
    zhttp_post_add(sad->http_spot, "frequency", sad->ssd->frequency);
    zhttp_post_add(sad->http_spot, "mode", sad->ssd->mode);
    zhttp_post_add(sad->http_spot, "comments", sad->ssd->comment);
    zhttp_post_add(sad->http_spot, "user", cfg->sota_user);
    tokenize(gs_token);
    zhttp_post_add(sad->http_spot, "token", gs_token->str);

    zhttp_post(sad->http_spot, zsel, "http://sota.emuf.cz:8044/spots/",
                    sota_api_spot_sent, sad);

    g_string_free(gs_token, TRUE);
    return 0;
}

static void sota_api_spot_sent(struct zhttp *http_spot)
{
    struct sota_api_data *sad = http_spot->arg;
    
    if (http_spot->errorstr)
    {
        log_addf("Network error: %s", http_spot->errorstr);
        zhttp_free0(http_spot);// sad->http = NULL;
        g_free(sad);
        return;
    }

    sad->error = zjson0_get_str(http_spot->datastr, -1, "error");
    sad->error_description = zjson0_get_str(http_spot->datastr, -1, "error_description");

    if (sad->error != NULL)
    {
        log_addf("SOTA %s: %s",sad->error, sad->error_description);
        sad->state = SOTA_SSO_ERROR;
        zhttp_free0(http_spot);// sad->http = NULL;
        g_free(sad);
        return;
    }

    sad->status = zjson0_get_str(http_spot->datastr, -1, "status");

    sad->ssd->id = zjson0_get_int(http_spot->datastr, -1, "id");
    sad->ssd->userID = zjson0_get_int(http_spot->datastr, -1, "userId");

    log_addf("SOTA %s (id=%i)", sad->status, sad->ssd->id);
    //log_addf("SOTA spot submitted (id=%i)", sad->ssd->id);
    sota_api_free(sad);
}

