/**
 * ABAP template: `<cls>.clas.locals_imp.abap`.
 *
 * Implementations for the four local classes declared in
 * `locals_def.abap`. Uses SAP kernel APIs that are part of the Steampunk
 * (s4-cloud) whitelist only:
 *   - cl_http_destination_provider=>create_by_destination
 *     (parameter name on cloud: `i_destination` (not `i_name` or `i_destination_name`))
 *   - cl_web_http_client_manager=>create_by_http_destination
 *   - if_web_http_request->set_binary  (NOT set_binary_body — Steampunk
 *     rejects with "Method SET_BINARY_BODY does not exist")
 *   - if_web_http_client verb constants typed as `if_web_http_client=>method`
 *     (SWITCH must produce the enum type, not `string`)
 *   - /ui2/cl_json=>serialize / deserialize (camelCase mode)
 *   - cl_abap_conv_codepage=>create_in / create_out
 *   - cl_web_http_utility->escape_url  (cl_http_utility is NOT permitted
 *     in ABAP Cloud)
 *   - cl_abap_typedescr=>describe_by_data  (DESCRIBE FIELD is disallowed
 *     in ABAP cloud; use RTTI)
 *
 * No reference to /ui2/cl_json=>generate, xco_cp_json, cl_http_client,
 * cl_http_utility, cl_rest_http_client, DESCRIBE FIELD, or any Z-prefixed
 * symbol.
 */
import type { LocalClassesNames } from './locals-def.abap';

export function buildLocalsImp(names: LocalClassesNames): string {
  const {
    localJsonClass: JSON_CLS,
    localJsonParserClass: JSON_PARSER,
    localResponseClass: RESPONSE,
    localHttpClass: HTTP,
  } = names;

  return `* Generated by @abapify/openai-codegen - local class implementations.
* ZERO Z-dependencies: uses SAP kernel APIs only (Steampunk whitelist).

CLASS ${HTTP} IMPLEMENTATION.

  METHOD constructor.
    me->destination = destination.
    me->server      = server.
  ENDMETHOD.

  METHOD build_query_string.
    DATA parts TYPE string_table.
    LOOP AT query ASSIGNING FIELD-SYMBOL(<q>).
      DATA(enc_n) = cl_web_http_utility=>escape_url( unescaped = <q>-name ).
      DATA(enc_v) = cl_web_http_utility=>escape_url( unescaped = <q>-value ).
      APPEND |{ enc_n }={ enc_v }| TO parts.
    ENDLOOP.
    result = concat_lines_of( table = parts sep = \`&\` ).
  ENDMETHOD.

  METHOD fetch.
    " Prefix with the configured server base path when set,
    " so OpenAPI specs with a non-root server still resolve correctly.
    DATA full_path TYPE string.
    full_path = COND string(
      WHEN server IS INITIAL THEN path
      ELSE |{ server }{ path }| ).
    IF query IS NOT INITIAL.
      full_path = |{ full_path }?{ build_query_string( query ) }|.
    ENDIF.

    DATA dest TYPE REF TO if_http_destination.
    dest = cl_http_destination_provider=>create_by_destination(
      i_destination = CONV #( destination ) ).

    DATA client TYPE REF TO if_web_http_client.
    client = cl_web_http_client_manager=>create_by_http_destination(
      i_destination = dest ).

    " All IO against client runs inside TRY ... CLEANUP so the HTTP
    " connection is closed even when execute() / binary read / codepage
    " conversion raise a kernel exception.
    TRY.
        DATA request TYPE REF TO if_web_http_request.
        request = client->get_http_request( ).
        request->set_uri_path( i_uri_path = full_path ).

        DATA verb TYPE if_web_http_client=>method.
        verb = SWITCH if_web_http_client=>method( method
          WHEN 'GET'     THEN if_web_http_client=>get
          WHEN 'POST'    THEN if_web_http_client=>post
          WHEN 'PUT'     THEN if_web_http_client=>put
          WHEN 'DELETE'  THEN if_web_http_client=>delete
          WHEN 'PATCH'   THEN if_web_http_client=>patch
          WHEN 'HEAD'    THEN if_web_http_client=>head
          WHEN 'OPTIONS' THEN if_web_http_client=>options
          ELSE if_web_http_client=>get ).

        LOOP AT headers ASSIGNING FIELD-SYMBOL(<h>).
          request->set_header_field( i_name  = <h>-name
                                     i_value = <h>-value ).
        ENDLOOP.

        IF binary IS NOT INITIAL.
          request->set_binary( i_data = binary ).
        ELSEIF body IS NOT INITIAL.
          request->set_text( i_text = body ).
        ENDIF.

        DATA resp TYPE REF TO if_web_http_response.
        resp = client->execute( i_method = verb ).

        DATA status_code TYPE i.
        status_code = resp->get_status( )-code.

        DATA body_bytes TYPE xstring.
        body_bytes = resp->get_binary( ).

        DATA body_text TYPE string.
        IF body_bytes IS NOT INITIAL.
          body_text = cl_abap_conv_codepage=>create_in( )->convert(
            source = body_bytes ).
        ENDIF.

        DATA raw_headers TYPE if_web_http_request=>name_value_pairs.
        raw_headers = resp->get_header_fields( ).

        DATA header_list TYPE kvs.
        LOOP AT raw_headers ASSIGNING FIELD-SYMBOL(<rh>).
          APPEND VALUE #( name  = <rh>-name
                          value = <rh>-value ) TO header_list.
        ENDLOOP.

        response = NEW ${RESPONSE}(
          status_code = status_code
          body_bytes  = body_bytes
          body_text   = body_text
          header_list = header_list ).

        client->close( ).
      CLEANUP.
        client->close( ).
    ENDTRY.
  ENDMETHOD.

ENDCLASS.

CLASS ${RESPONSE} IMPLEMENTATION.

  METHOD constructor.
    me->status_code = status_code.
    me->body_bytes  = body_bytes.
    me->body_text   = body_text.
    me->header_list = header_list.
  ENDMETHOD.

  METHOD status.
    result = status_code.
  ENDMETHOD.

  METHOD body.
    result = body_bytes.
  ENDMETHOD.

  METHOD text.
    result = body_text.
  ENDMETHOD.

  METHOD headers.
    result = header_list.
  ENDMETHOD.

  METHOD header.
    DATA needle TYPE string.
    needle = to_lower( name ).
    LOOP AT header_list ASSIGNING FIELD-SYMBOL(<h>).
      IF to_lower( <h>-name ) = needle.
        result = <h>-value.
        RETURN.
      ENDIF.
    ENDLOOP.
  ENDMETHOD.

ENDCLASS.

CLASS ${JSON_PARSER} IMPLEMENTATION.

  METHOD constructor.
    me->json_text = json_text.
  ENDMETHOD.

  METHOD to.
    FIELD-SYMBOLS <t> TYPE any.
    ASSIGN target->* TO <t>.
    /ui2/cl_json=>deserialize(
      EXPORTING json        = json_text
                pretty_name = /ui2/cl_json=>pretty_mode-camel_case
      CHANGING  data        = <t> ).
  ENDMETHOD.

ENDCLASS.

CLASS ${JSON_CLS} IMPLEMENTATION.

  METHOD stringify.
    result = /ui2/cl_json=>serialize(
      data        = data
      pretty_name = /ui2/cl_json=>pretty_mode-camel_case
      compress    = abap_true ).
  ENDMETHOD.

  METHOD render.
    DATA text TYPE string.
    text = stringify( data ).
    result = cl_abap_conv_codepage=>create_out( )->convert(
      source = text ).
  ENDMETHOD.

  METHOD parse.
    DATA text TYPE string.
    FIELD-SYMBOLS <s> TYPE any.
    ASSIGN source TO <s>.
    " RTTI dispatch — static type inspection is used instead of the
    " legacy DESCRIBE statement, which is not allowed on ABAP cloud.
    DATA type_info TYPE REF TO cl_abap_typedescr.
    type_info = cl_abap_typedescr=>describe_by_data( <s> ).
    IF type_info->type_kind = cl_abap_typedescr=>typekind_xstring.
      DATA bytes TYPE xstring.
      bytes = <s>.
      text = cl_abap_conv_codepage=>create_in( )->convert(
        source = bytes ).
    ELSE.
      text = <s>.
    ENDIF.
    result = NEW ${JSON_PARSER}( json_text = text ).
  ENDMETHOD.

ENDCLASS.
`;
}
