/*
 * Copyright (C) 2019-2025 by Sukchan Lee <acetcom@gmail.com>
 *
 * This file is part of Open5GS.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#include "sbi-path.h"

#include "nudr-handler.h"

bool pcf_nudr_dr_handle_query_am_data(
    pcf_ue_am_t *pcf_ue_am,
    ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg)
{
    int rv, status = 0;
    char *strerror = NULL;
    ogs_sbi_server_t *server = NULL;

    ogs_sbi_message_t sendmsg;
    ogs_sbi_header_t header;
    ogs_sbi_response_t *response = NULL;

    ogs_subscription_data_t subscription_data;

    ogs_assert(pcf_ue_am);
    ogs_assert(stream);
    server = ogs_sbi_server_from_stream(stream);
    ogs_assert(server);

    ogs_assert(recvmsg);

    memset(&subscription_data, 0, sizeof(ogs_subscription_data_t));

    SWITCH(recvmsg->h.resource.component[3])
    CASE(OGS_SBI_RESOURCE_NAME_AM_DATA)
        OpenAPI_policy_association_t PolicyAssociation;
        OpenAPI_ambr_t UeAmbr;
        OpenAPI_list_t *TriggerList = NULL;
        OpenAPI_lnode_t *node = NULL;

        if (!recvmsg->AmPolicyData) {
            strerror = ogs_msprintf("[%s] No AmPolicyData", pcf_ue_am->supi);
            status = OGS_SBI_HTTP_STATUS_BAD_REQUEST;
            goto cleanup;
        }

        if (!pcf_ue_am->policy_association_request) {
            strerror = ogs_msprintf("[%s] No PolicyAssociationRequest",
                    pcf_ue_am->supi);
            status = OGS_SBI_HTTP_STATUS_BAD_REQUEST;
            goto cleanup;
        }

        rv = ogs_dbi_subscription_data(pcf_ue_am->supi, &subscription_data);
        if (rv != OGS_OK) {
            strerror = ogs_msprintf("[%s] Cannot find SUPI in DB",
                    pcf_ue_am->supi);
            status = OGS_SBI_HTTP_STATUS_NOT_FOUND;
            goto cleanup;
        }

        if (!subscription_data.ambr.uplink &&
                !subscription_data.ambr.downlink) {
            strerror = ogs_msprintf("[%s] No UE-AMBR", pcf_ue_am->supi);
            status = OGS_SBI_HTTP_STATUS_NOT_FOUND;
            goto cleanup;
        }

        memset(&PolicyAssociation, 0, sizeof(PolicyAssociation));
        PolicyAssociation.request = pcf_ue_am->policy_association_request;
        PolicyAssociation.supp_feat =
            ogs_uint64_to_string(pcf_ue_am->am_policy_control_features);
        ogs_assert(PolicyAssociation.supp_feat);

        TriggerList = OpenAPI_list_create();
        ogs_assert(TriggerList);

        memset(&UeAmbr, 0, sizeof(UeAmbr));
        if (OGS_SBI_FEATURES_IS_SET(pcf_ue_am->am_policy_control_features,
                    OGS_SBI_NPCF_AM_POLICY_CONTROL_UE_AMBR_AUTHORIZATION)) {
            if (pcf_ue_am->subscribed_ue_ambr) {
                ogs_bitrate_t subscribed_ue_ambr;

                subscribed_ue_ambr.uplink = ogs_sbi_bitrate_from_string(
                        pcf_ue_am->subscribed_ue_ambr->uplink);
                subscribed_ue_ambr.downlink = ogs_sbi_bitrate_from_string(
                        pcf_ue_am->subscribed_ue_ambr->downlink);

                if (((subscribed_ue_ambr.uplink / 1000) !=
                     (subscription_data.ambr.uplink / 1000)) ||
                    ((subscribed_ue_ambr.downlink / 1000) !=
                     (subscription_data.ambr.downlink / 1000))) {

                    OpenAPI_list_add(TriggerList,
                            (void *)OpenAPI_request_trigger_UE_AMBR_CH);
                }

                UeAmbr.uplink = ogs_sbi_bitrate_to_string(
                        subscription_data.ambr.uplink, OGS_SBI_BITRATE_KBPS);
                UeAmbr.downlink = ogs_sbi_bitrate_to_string(
                        subscription_data.ambr.downlink, OGS_SBI_BITRATE_KBPS);
                PolicyAssociation.ue_ambr = &UeAmbr;
            }
        }

        if (TriggerList->count)
            PolicyAssociation.triggers = TriggerList;

        memset(&header, 0, sizeof(header));
        header.service.name =
            OpenAPI_service_name_ToString(
                    OpenAPI_service_name_npcf_am_policy_control);
        header.api.version = (char *)OGS_SBI_API_V1;
        header.resource.component[0] =
            (char *)OGS_SBI_RESOURCE_NAME_POLICIES;
        header.resource.component[1] = pcf_ue_am->association_id;

        memset(&sendmsg, 0, sizeof(sendmsg));
        sendmsg.PolicyAssociation = &PolicyAssociation;
        sendmsg.http.location = ogs_sbi_server_uri(server, &header);

        response = ogs_sbi_build_response(
                &sendmsg, OGS_SBI_HTTP_STATUS_CREATED);
        ogs_assert(response);
        ogs_assert(true == ogs_sbi_server_send_response(stream, response));

        ogs_free(sendmsg.http.location);

        ogs_free(PolicyAssociation.supp_feat);

        OpenAPI_list_free(TriggerList);

        if (UeAmbr.uplink)
            ogs_free(UeAmbr.uplink);
        if (UeAmbr.downlink)
            ogs_free(UeAmbr.downlink);

        ogs_subscription_data_free(&subscription_data);

        OpenAPI_list_for_each(
                PolicyAssociation.request->allowed_snssais, node) {
            struct OpenAPI_snssai_s *Snssai = node->data;
            if (Snssai) {
                ogs_s_nssai_t s_nssai;
                s_nssai.sst = Snssai->sst;
                s_nssai.sd = ogs_s_nssai_sd_from_string(Snssai->sd);

                pcf_metrics_inst_by_slice_add(&pcf_ue_am->guami.plmn_id,
                        &s_nssai, PCF_METR_CTR_PA_POLICYAMASSOSUCC, 1);
            } else {
                ogs_error("[%s] No Snssai", pcf_ue_am->supi);
            }
        }

        return true;

    DEFAULT
        strerror = ogs_msprintf("[%s] Invalid resource name [%s]", 
                        pcf_ue_am->supi, recvmsg->h.resource.component[3]);
    END

cleanup:
    ogs_assert(strerror);
    ogs_assert(status);
    ogs_error("%s", strerror);
    ogs_assert(true ==
        ogs_sbi_server_send_error(stream, status, recvmsg, strerror,
                NULL, NULL));
    ogs_free(strerror);

    ogs_subscription_data_free(&subscription_data);

    return false;
}

bool pcf_nudr_dr_handle_query_sm_data(
    pcf_sess_t *sess, ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg)
{
    int status = 0;
    char *strerror = NULL;
    pcf_ue_sm_t *pcf_ue_sm = NULL;
    ogs_sbi_server_t *server = NULL;
    int r;

    ogs_assert(sess);
    pcf_ue_sm = pcf_ue_sm_find_by_id(sess->pcf_ue_sm_id);
    ogs_assert(pcf_ue_sm);
    ogs_assert(stream);
    server = ogs_sbi_server_from_stream(stream);
    ogs_assert(server);

    ogs_assert(recvmsg);

    SWITCH(recvmsg->h.resource.component[3])
    CASE(OGS_SBI_RESOURCE_NAME_SM_DATA)
        if (!recvmsg->SmPolicyData) {
            strerror = ogs_msprintf("[%s:%d] No SmPolicyData",
                    pcf_ue_sm->supi, sess->psi);
            status = OGS_SBI_HTTP_STATUS_BAD_REQUEST;
            goto cleanup;
        }

        r = pcf_sess_sbi_discover_and_send(
                    OpenAPI_service_name_nbsf_management, NULL,
                    pcf_nbsf_management_build_register,
                    sess, stream, NULL);
        ogs_expect(r == OGS_OK);
        ogs_assert(r != OGS_ERROR);

        return true;

    DEFAULT
        strerror = ogs_msprintf("[%s:%d] Invalid resource name [%s]", 
                    pcf_ue_sm->supi, sess->psi,
                    recvmsg->h.resource.component[3]);
    END

cleanup:
    ogs_assert(strerror);
    status = OGS_SBI_HTTP_STATUS_FORBIDDEN;
    ogs_error("%s", strerror);
    /*
     * TS29.512
     * 4.2.2.2 SM Policy Association establishment 
     *
     * If the PCF, based on local configuration and/or operator
     * policies, denies the creation of the Individual SM Policy
     * resource, the PCF may reject the request and include in
     * an HTTP "403 Forbidden" response message the "cause"
     * attribute of the ProblemDetails data structure set to
     * "POLICY_CONTEXT_DENIED".
     */
    ogs_assert(true ==
        ogs_sbi_server_send_error(stream, status, recvmsg, strerror,
                NULL, "POLICY_CONTEXT_DENIED"));
    ogs_free(strerror);

    return false;
}
