/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @file axis2_json_msg_formatter.c
 * @brief JSON Message Formatter Implementation - Following Axis2/Java Pattern
 *
 * Axis2/Java JSONFormatter Architecture:
 * 1. Extract JSON response from MessageContext properties
 * 2. Write pure JSON to HTTP response stream
 * 3. Set application/json content-type
 * 4. No AXIOM→JSON conversion (pure JSON output)
 *
 * Performance: Direct JSON streaming, minimal memory usage
 */

#include "axis2_json_msg_formatter.h"
#include <axutil_property.h>
#include <string.h>

/**
 * @brief JSON Message Formatter structure
 * Following Axis2/C message formatter pattern
 */
struct axis2_json_msg_formatter
{
    axis2_msg_formatter_t msg_formatter;
};

/* Forward declarations */
static axis2_status_t AXIS2_CALL
json_msg_formatter_write_to(
    axis2_msg_formatter_t* msg_formatter,
    const axutil_env_t* env,
    axis2_msg_ctx_t* msg_ctx,
    axutil_stream_t* out_stream,
    axis2_bool_t preserve_soap_headers);

static axis2_char_t* AXIS2_CALL
json_msg_formatter_get_content_type(
    axis2_msg_formatter_t* msg_formatter,
    const axutil_env_t* env,
    axis2_msg_ctx_t* msg_ctx);

static void AXIS2_CALL
json_msg_formatter_free(
    axis2_msg_formatter_t* msg_formatter,
    const axutil_env_t* env);

/**
 * @brief Create JSON Message Formatter
 * Following Axis2/Java JSONFormatter creation pattern
 */
AXIS2_EXTERN axis2_msg_formatter_t* AXIS2_CALL
axis2_json_msg_formatter_create(const axutil_env_t* env)
{
    struct axis2_json_msg_formatter* json_formatter = NULL;
    axis2_msg_formatter_t* msg_formatter = NULL;

    AXIS2_LOG_INFO(env->log, "Creating JSON Message Formatter");

    json_formatter = (struct axis2_json_msg_formatter*)AXIS2_MALLOC(env->allocator,
                                                                   sizeof(struct axis2_json_msg_formatter));
    if (!json_formatter) {
        AXIS2_ERROR_SET(env->error, AXIS2_ERROR_NO_MEMORY, AXIS2_FAILURE);
        return NULL;
    }
    memset(json_formatter, 0, sizeof(struct axis2_json_msg_formatter));

    msg_formatter = &(json_formatter->msg_formatter);

    /* Set function pointers following Axis2/C pattern */
    axis2_msg_formatter_set_write_to(msg_formatter, env, json_msg_formatter_write_to);
    axis2_msg_formatter_set_get_content_type(msg_formatter, env, json_msg_formatter_get_content_type);
    axis2_msg_formatter_set_free(msg_formatter, env, json_msg_formatter_free);

    AXIS2_LOG_INFO(env->log, "JSON Message Formatter created successfully");
    return msg_formatter;
}

/**
 * @brief Write JSON response to HTTP stream
 * Following Axis2/Java JSONFormatter.writeTo() logic
 */
static axis2_status_t AXIS2_CALL
json_msg_formatter_write_to(
    axis2_msg_formatter_t* msg_formatter,
    const axutil_env_t* env,
    axis2_msg_ctx_t* msg_ctx,
    axutil_stream_t* out_stream,
    axis2_bool_t preserve_soap_headers)
{
    axutil_property_t* json_response_prop = NULL;
    axis2_char_t* json_response = NULL;
    int json_len = 0;
    int bytes_written = 0;

    AXIS2_LOG_INFO(env->log, "JSON Message Formatter: Writing JSON response");

    if (!msg_ctx || !out_stream) {
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Message context or output stream is null");
        return AXIS2_FAILURE;
    }

    /* Extract JSON response from MessageContext properties (Axis2/Java pattern) */
    json_response_prop = axis2_msg_ctx_get_property(msg_ctx, env, AXIS2_JSON_RESPONSE_PROPERTY);
    if (!json_response_prop) {
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI,
                       "JSON response property not found - service should set this");

        /* Write default error response */
        const axis2_char_t* default_error = "{\"error\": \"No JSON response generated by service\"}";
        json_len = strlen(default_error);
        bytes_written = axutil_stream_write(out_stream, env, default_error, json_len);

        if (bytes_written > 0) {
            AXIS2_LOG_INFO(env->log,
                           "Wrote default error response (%d bytes)", bytes_written);
            return AXIS2_SUCCESS;
        }
        return AXIS2_FAILURE;
    }

    json_response = (axis2_char_t*)axutil_property_get_value(json_response_prop, env);
    if (!json_response) {
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "JSON response property value is null");
        return AXIS2_FAILURE;
    }

    /* Write pure JSON to HTTP stream (no SOAP envelope) */
    json_len = strlen(json_response);
    bytes_written = axutil_stream_write(out_stream, env, json_response, json_len);

    if (bytes_written > 0) {
        AXIS2_LOG_INFO(env->log,
                      "JSON Message Formatter: Successfully wrote %d bytes of JSON response",
                      bytes_written);

        /* Log response for debugging */
        AXIS2_LOG_INFO(env->log,
                       "JSON Response: %s", json_response);

        return AXIS2_SUCCESS;
    } else {
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI,
                       "Failed to write JSON response to stream");
        return AXIS2_FAILURE;
    }
}

/**
 * @brief Get JSON content type
 * Following Axis2/Java JSONFormatter.getContentType() pattern
 */
static axis2_char_t* AXIS2_CALL
json_msg_formatter_get_content_type(
    axis2_msg_formatter_t* msg_formatter,
    const axutil_env_t* env,
    axis2_msg_ctx_t* msg_ctx)
{
    AXIS2_LOG_INFO(env->log,
                   "JSON Message Formatter: Returning content-type: %s", AXIS2_JSON_CONTENT_TYPE);

    return axutil_strdup(env, AXIS2_JSON_CONTENT_TYPE);
}

/**
 * @brief Free JSON Message Formatter
 */
static void AXIS2_CALL
json_msg_formatter_free(
    axis2_msg_formatter_t* msg_formatter,
    const axutil_env_t* env)
{
    if (msg_formatter) {
        AXIS2_FREE(env->allocator, msg_formatter);
    }
}