<?php
/**
 * Automatic translation handler for OpenAI requests.
 *
 * @package WPR_Translate
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * Run automatic translation using OpenAI.
 *
 * @param int    $post_id      Post ID.
 * @param string $language     Language code.
 * @param array  $payload      Structured translation payload.
 * @param string $payload_json JSON encoded payload.
 *
 * @return true|WP_Error
 */
function wpr_translate_auto_translate_post_openai( $post_id, $language, $payload = array(), $payload_json = '' ) {
    // Normalize the request arguments to ensure we work with valid data.
    $post_id  = absint( $post_id );
    $language = sanitize_text_field( $language );
    $payload  = is_array( $payload ) ? $payload : array();

    if ( $post_id <= 0 ) {
        return new WP_Error( 'wpr_translate_invalid_post', __( 'Invalid post selected for translation.', 'wpr-translate' ) );
    }

    // Build a focused translation payload so the model receives only data that needs translation.
    $translation_payload = array(
        'post_id'  => $post_id,
        'language' => $language,
    );

    if ( isset( $payload['post_type'] ) ) {
        $translation_payload['post_type'] = sanitize_key( $payload['post_type'] );
    }

    $translation_payload['post'] = array();

    if ( isset( $payload['post'] ) && is_array( $payload['post'] ) ) {
        foreach ( array( 'post_title', 'post_excerpt', 'post_content' ) as $field_key ) {
            if ( ! isset( $payload['post'][ $field_key ] ) ) {
                continue;
            }

            $translation_payload['post'][ $field_key ] = (string) $payload['post'][ $field_key ];
        }
    }

    if ( empty( $translation_payload['post'] ) ) {
        unset( $translation_payload['post'] );
    }

    $meta_source = array();

    if ( isset( $payload['post_meta_fields'] ) && is_array( $payload['post_meta_fields'] ) ) {
        $meta_source = $payload['post_meta_fields'];
    } elseif ( isset( $payload['custom_fields'] ) && is_array( $payload['custom_fields'] ) ) {
        $meta_source = $payload['custom_fields'];
    }

    if ( ! empty( $meta_source ) ) {
        $translation_payload['post_meta_fields'] = array();

        foreach ( $meta_source as $meta_key => $meta_values ) {
            $raw_meta_key = is_string( $meta_key ) ? $meta_key : '';

            if ( '' === $raw_meta_key ) {
                continue;
            }

            $translation_payload['post_meta_fields'][ $raw_meta_key ] = $meta_values;
        }

        if ( empty( $translation_payload['post_meta_fields'] ) ) {
            unset( $translation_payload['post_meta_fields'] );
        }
    }

    if ( isset( $payload['taxonomies'] ) && is_array( $payload['taxonomies'] ) ) {
        $translation_payload['taxonomy'] = array();

        foreach ( $payload['taxonomies'] as $taxonomy_key => $taxonomy_data ) {
            $sanitized_taxonomy = sanitize_key( $taxonomy_key );

            if ( '' === $sanitized_taxonomy ) {
                continue;
            }

            $translation_payload['taxonomy'][ $sanitized_taxonomy ] = $taxonomy_data;
        }

        if ( empty( $translation_payload['taxonomy'] ) ) {
            unset( $translation_payload['taxonomy'] );
        }
    }

    // Keep source taxonomy structures for fallback merging after the LLM response.
    $source_taxonomy_payload = array();

    if ( isset( $translation_payload['taxonomy'] ) && is_array( $translation_payload['taxonomy'] ) ) {
        $source_taxonomy_payload = $translation_payload['taxonomy'];
        $translation_payload['taxonomy'] = wpr_translate_prepare_taxonomy_payload_for_llm( $translation_payload['taxonomy'] );

        if ( empty( $translation_payload['taxonomy'] ) ) {
            unset( $translation_payload['taxonomy'] );
        }
    }

    // Preserve Elementor widget translation data so translated widgets can be applied later.
    if ( isset( $payload['elementor_translate'] ) && is_array( $payload['elementor_translate'] ) ) {
        $translation_payload['elementor_translate'] = $payload['elementor_translate'];

        if ( isset( $translation_payload['elementor_translate']['taxonomies'] ) && is_array( $translation_payload['elementor_translate']['taxonomies'] ) ) {
            $translation_payload['elementor_translate']['taxonomies'] = wpr_translate_prepare_taxonomy_payload_for_llm( $translation_payload['elementor_translate']['taxonomies'] );
        }

        // Keep translated post-id map local; taxonomy terms remain translatable.
        if ( isset( $translation_payload['elementor_translate']['translated_post_id'] ) ) {
            unset( $translation_payload['elementor_translate']['translated_post_id'] );
        }

        if ( empty( $translation_payload['elementor_translate'] ) ) {
            unset( $translation_payload['elementor_translate'] );
        }
    }

    if ( isset( $payload['post_id'] ) ) {
        $translation_payload['original_post_id'] = absint( $payload['post_id'] );
    }

    // Log only the Wpresidence_Grids Elementor payload that is sent to the LLM.
    if ( isset( $translation_payload['elementor_translate'] ) && is_array( $translation_payload['elementor_translate'] ) ) {
        wpr_translate_log_openai_elementor_widget_payload( 'send_llm', $post_id, $language, $translation_payload['elementor_translate'] );
    }

    // Ensure we have a JSON representation of the payload to feed to the model.
    $payload_json = wp_json_encode( $translation_payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES );

    if ( false === $payload_json || null === $payload_json ) {
        return new WP_Error( 'wpr_translate_payload_encoding_failed', __( 'Failed to prepare the translation payload.', 'wpr-translate' ) );
    }

    // Debug trace: payload sent to the LLM.
    wpr_translate_openai_error_log(
        'send_payload',
        array(
            'post_id'                 => $post_id,
            'requested_language'      => $language,
            'translation_payload'     => $translation_payload,
            'source_taxonomy_payload' => $source_taxonomy_payload,
            'payload_json'            => $payload_json,
        )
    );

    // Translate the prepared JSON payload through the shared OpenAI helper.
    $translated_payload = wpr_translate_openai_translate_json_payload( $language, $payload_json, 'posts' );

    if ( is_wp_error( $translated_payload ) ) {
        wpr_translate_openai_error_log(
            'receive_payload_error',
            array(
                'post_id'            => $post_id,
                'requested_language' => $language,
                'error_code'         => $translated_payload->get_error_code(),
                'error_message'      => $translated_payload->get_error_message(),
            )
        );
        return $translated_payload;
    }

    // Debug trace: raw payload received from the LLM.
    wpr_translate_openai_error_log(
        'receive_payload_raw',
        array(
            'post_id'            => $post_id,
            'requested_language' => $language,
            'translated_payload' => $translated_payload,
        )
    );

    if ( function_exists( 'wpr_translate_normalize_elementor_translation_response' ) ) {
        $translated_payload = wpr_translate_normalize_elementor_translation_response( $translated_payload );
    }

    // Keep the requested target language authoritative so taxonomy term
    // mappings are stored under the expected language code.
    $requested_language = strtolower( sanitize_key( $language ) );

    if ( '' !== $requested_language && function_exists( 'wpr_translate_get_language' ) ) {
        $requested_language_data = wpr_translate_get_language( $requested_language );

        if ( is_array( $requested_language_data ) && ! empty( $requested_language_data['code'] ) ) {
            $requested_language = strtolower( sanitize_key( $requested_language_data['code'] ) );
        }
    }

    if ( '' !== $requested_language ) {
        $translated_payload['language'] = $requested_language;
    }

    // Merge source taxonomy payload as fallback while keeping translated term text.
    if ( ! empty( $source_taxonomy_payload ) ) {
        $target_taxonomy_payload = array();

        if ( isset( $translated_payload['taxonomy'] ) && is_array( $translated_payload['taxonomy'] ) ) {
            $target_taxonomy_payload = $translated_payload['taxonomy'];
        }

        $translated_payload['taxonomy'] = wpr_translate_merge_elementor_taxonomy_payloads( $source_taxonomy_payload, $target_taxonomy_payload );
    }

    // Preserve source Elementor taxonomy and related-ID payloads when model output omits them.
    if (
        isset( $payload['elementor_translate'] )
        && is_array( $payload['elementor_translate'] )
    ) {
        if ( ! isset( $translated_payload['elementor_translate'] ) || ! is_array( $translated_payload['elementor_translate'] ) ) {
            $translated_payload['elementor_translate'] = array();
        }

        if (
            isset( $payload['elementor_translate']['taxonomies'] )
            && is_array( $payload['elementor_translate']['taxonomies'] )
        ) {
            $source_taxonomies = $payload['elementor_translate']['taxonomies'];
            $target_taxonomies = array();

            if ( isset( $translated_payload['elementor_translate']['taxonomies'] ) && is_array( $translated_payload['elementor_translate']['taxonomies'] ) ) {
                $target_taxonomies = $translated_payload['elementor_translate']['taxonomies'];
            }

            $translated_payload['elementor_translate']['taxonomies'] = wpr_translate_merge_elementor_taxonomy_payloads( $source_taxonomies, $target_taxonomies );
        }

        if (
            isset( $payload['elementor_translate']['translated_post_id'] )
            && is_array( $payload['elementor_translate']['translated_post_id'] )
        ) {
            $source_ids = $payload['elementor_translate']['translated_post_id'];
            $target_ids = array();

            if ( isset( $translated_payload['elementor_translate']['translated_post_id'] ) && is_array( $translated_payload['elementor_translate']['translated_post_id'] ) ) {
                $target_ids = $translated_payload['elementor_translate']['translated_post_id'];
            }

            $translated_payload['elementor_translate']['translated_post_id'] = wpr_translate_merge_elementor_taxonomy_payloads( $source_ids, $target_ids );
        }
    }

    // Preserve source post ID for taxonomy resolution at apply stage.
    if ( isset( $payload['post_id'] ) ) {
        $translated_payload['_wpr_source_post_id'] = absint( $payload['post_id'] );
    }

    // Log only the Wpresidence_Grids Elementor payload received from the LLM.
    if ( isset( $translated_payload['elementor_translate'] ) && is_array( $translated_payload['elementor_translate'] ) ) {
        wpr_translate_log_openai_elementor_widget_payload( 'receive_llm', $post_id, $language, $translated_payload['elementor_translate'] );
    }

    // Debug trace: final payload applied to translated post.
    wpr_translate_openai_error_log(
        'receive_payload_final',
        array(
            'post_id'            => $post_id,
            'requested_language' => $language,
            'translated_payload' => $translated_payload,
        )
    );

    return wpr_translate_apply_translated_post_data( $post_id, $translated_payload );
}

/**
 * Write OpenAI translation debug information to error log using print_r.
 *
 * @param string $stage   Debug stage label.
 * @param mixed  $payload Debug payload.
 *
 * @return void
 */
function wpr_translate_openai_error_log( $stage, $payload ) {
    error_log( '[WPR Translate][OpenAI][' . sanitize_key( (string) $stage ) . '] ' . print_r( $payload, true ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
}

/**
 * Remove term identity fields from taxonomy payloads sent to the LLM.
 *
 * Term IDs and slugs remain available from source payload fallback merging.
 *
 * @param array $taxonomy_payload Taxonomy payload section.
 *
 * @return array
 */
function wpr_translate_prepare_taxonomy_payload_for_llm( $taxonomy_payload ) {
    $taxonomy_payload = is_array( $taxonomy_payload ) ? $taxonomy_payload : array();

    return wpr_translate_strip_term_identity_fields_recursive( $taxonomy_payload );
}

/**
 * Recursively strip `term_id` and `slug` from term-like entries.
 *
 * @param mixed $value Payload fragment.
 *
 * @return mixed
 */
function wpr_translate_strip_term_identity_fields_recursive( $value ) {
    if ( ! is_array( $value ) ) {
        return $value;
    }

    $normalized = array();

    foreach ( $value as $key => $child ) {
        $normalized[ $key ] = wpr_translate_strip_term_identity_fields_recursive( $child );
    }

    if ( isset( $normalized['name'] ) && ( array_key_exists( 'term_id', $normalized ) || array_key_exists( 'slug', $normalized ) ) ) {
        unset( $normalized['term_id'] );
        unset( $normalized['slug'] );
    }

    return $normalized;
}

/**
 * Normalize Elementor translation response shape returned by the model.
 *
 * @param array $translated_payload Decoded model response payload.
 *
 * @return array
 */
function wpr_translate_normalize_elementor_translation_response( $translated_payload ) {
    $translated_payload = is_array( $translated_payload ) ? $translated_payload : array();

    if ( empty( $translated_payload['elementor_translate'] ) || ! is_array( $translated_payload['elementor_translate'] ) ) {
        return $translated_payload;
    }

    $elementor_translate = $translated_payload['elementor_translate'];

    // Some responses move these sections to the root level; move them back.
    foreach ( array( 'translated_post_id', 'taxonomies' ) as $section_key ) {
        if ( isset( $translated_payload[ $section_key ] ) && is_array( $translated_payload[ $section_key ] ) && ! isset( $elementor_translate[ $section_key ] ) ) {
            $elementor_translate[ $section_key ] = $translated_payload[ $section_key ];
        }
    }

    if ( ! isset( $elementor_translate['translated_strings'] ) || ! is_array( $elementor_translate['translated_strings'] ) ) {
        $elementor_translate['translated_strings'] = array();
    }

    // Some responses place widget translations directly under elementor_translate.
    foreach ( $elementor_translate as $widget_key => $widget_value ) {
        if ( in_array( $widget_key, array( 'translated_strings', 'translated_post_id', 'taxonomies' ), true ) ) {
            continue;
        }

        if ( is_array( $widget_value ) && ! isset( $elementor_translate['translated_strings'][ $widget_key ] ) ) {
            $elementor_translate['translated_strings'][ $widget_key ] = $widget_value;
            unset( $elementor_translate[ $widget_key ] );
        }
    }

    $translated_payload['elementor_translate'] = $elementor_translate;

    if ( isset( $translated_payload['translated_post_id'] ) ) {
        unset( $translated_payload['translated_post_id'] );
    }

    if ( isset( $translated_payload['taxonomies'] ) ) {
        unset( $translated_payload['taxonomies'] );
    }

    return $translated_payload;
}

/**
 * Extract one Elementor widget payload for debug logging.
 *
 * @param array  $elementor_translate Elementor payload sections.
 * @param string $widget_type         Widget type key.
 *
 * @return array
 */
function wpr_translate_extract_elementor_widget_payload_for_log( $elementor_translate, $widget_type ) {
    $elementor_translate = is_array( $elementor_translate ) ? $elementor_translate : array();
    $widget_type         = sanitize_text_field( $widget_type );
    $output              = array();

    foreach ( array( 'translated_strings', 'taxonomies', 'translated_post_id' ) as $section_key ) {
        if (
            isset( $elementor_translate[ $section_key ][ $widget_type ] )
            && is_array( $elementor_translate[ $section_key ][ $widget_type ] )
        ) {
            $output[ $section_key ] = array(
                $widget_type => $elementor_translate[ $section_key ][ $widget_type ],
            );
        }
    }

    return $output;
}

/**
 * Log OpenAI Elementor payload flow for one widget.
 *
 * @param string $stage              Flow stage label.
 * @param int    $post_id            Target post ID.
 * @param string $language           Target language code.
 * @param array  $elementor_translate Elementor payload sections.
 *
 * @return void
 */
function wpr_translate_log_openai_elementor_widget_payload( $stage, $post_id, $language, $elementor_translate ) {
    $stage   = sanitize_key( $stage );
    $post_id = absint( $post_id );
    $language = sanitize_text_field( $language );
    $widget_payload = wpr_translate_extract_elementor_widget_payload_for_log( $elementor_translate, 'Wpresidence_Grids' );

    if ( empty( $widget_payload ) ) {
        return;
    }

    // Debug logging intentionally disabled.
    return;
}

/**
 * Merge nested Elementor section payloads while preserving missing source fields.
 *
 * @param array $source_section Source payload section built before model translation.
 * @param array $target_section Target payload section returned by the model.
 *
 * @return array
 */
function wpr_translate_merge_elementor_taxonomy_payloads( $source_section, $target_section ) {
    $source_section = is_array( $source_section ) ? $source_section : array();
    $target_section = is_array( $target_section ) ? $target_section : array();
    return wpr_translate_recursive_merge_payload_section( $source_section, $target_section );
}

/**
 * Recursively merge payload arrays while preserving source-only keys.
 *
 * @param mixed $source_value Source payload value.
 * @param mixed $target_value Target payload value.
 *
 * @return mixed
 */
function wpr_translate_recursive_merge_payload_section( $source_value, $target_value ) {
    if ( ! is_array( $target_value ) ) {
        return $target_value;
    }

    $source_value = is_array( $source_value ) ? $source_value : array();
    $merged       = $source_value;

    foreach ( $target_value as $key => $target_child ) {
        if ( isset( $merged[ $key ] ) && is_array( $merged[ $key ] ) && is_array( $target_child ) ) {
            $merged[ $key ] = wpr_translate_recursive_merge_payload_section( $merged[ $key ], $target_child );
            continue;
        }

        $merged[ $key ] = $target_child;
    }

    return $merged;
}

/**
 * Translate a generic JSON payload using OpenAI and return decoded JSON.
 *
 * @param string $language         Target language code.
 * @param string $payload_json     JSON string payload.
 * @param string $settings_context Optional context label for internal tracking.
 *
 * @return array|WP_Error
 */
function wpr_translate_openai_translate_json_payload( $language, $payload_json, $settings_context = 'default' ) {
    $language         = sanitize_text_field( $language );
    $payload_json     = is_string( $payload_json ) ? $payload_json : '';
    $settings_context = sanitize_key( $settings_context );

    if ( '' === $payload_json ) {
        return new WP_Error( 'wpr_translate_payload_encoding_failed', __( 'Failed to prepare the translation payload.', 'wpr-translate' ) );
    }

    // Load OpenAI credentials and prompts from plugin settings.
    $settings = get_option( 'wpr_translate_auto_translation', array() );
    $settings = is_array( $settings ) ? $settings : array();
    $api_key  = isset( $settings['openai_api_key'] ) ? trim( (string) $settings['openai_api_key'] ) : '';

    if ( '' === $api_key ) {
        return new WP_Error( 'wpr_translate_missing_openai_key', __( 'OpenAI API key is not configured.', 'wpr-translate' ) );
    }

    $endpoint                 = 'https://api.openai.com/v1/chat/completions';
    $default_system_prompt    = 'You are a translator that converts WordPress post data into the requested language.';
    $configured_system_prompt = isset( $settings['openai_system_prompt'] ) ? $settings['openai_system_prompt'] : '';
    $system_prompt_base       = '' !== $configured_system_prompt ? $configured_system_prompt : $default_system_prompt;
    $system_prompt            = trim( $system_prompt_base . ' Keep the JSON structure identical. Respond with valid JSON only.' );

    $default_user_prompt    = 'Translate the following WordPress post payload into the %1$s language.';
    $configured_user_prompt = isset( $settings['openai_user_prompt'] ) ? $settings['openai_user_prompt'] : '';
    $user_prompt_template   = '' !== $configured_user_prompt ? $configured_user_prompt : $default_user_prompt;
    $user_prompt_prefix     = 'strings' === $settings_context ? 'This request contains WordPress string translations.' : '';
    $user_prompt            = sprintf(
        /* translators: 1: Target language code. 2: JSON payload. */
        $user_prompt_template . ' Preserve the JSON structure and keys. Respond with JSON only.%4$s%3$s%2$s',
        $language ? $language : __( 'requested', 'wpr-translate' ),
        $payload_json,
        PHP_EOL . PHP_EOL,
        '' !== $user_prompt_prefix ? $user_prompt_prefix . PHP_EOL . PHP_EOL : ''
    );

    $request_body = array(
        'model'           => 'gpt-4o-mini',
        'response_format' => array( 'type' => 'json_object' ),
        'messages'        => array(
            array(
                'role'    => 'system',
                'content' => $system_prompt,
            ),
            array(
                'role'    => 'user',
                'content' => $user_prompt,
            ),
        ),
    );

    $attempts     = 0;
    $max_attempts = 3;
    $response     = null;

    while ( $attempts < $max_attempts ) {
        $attempts++;

        $response = wp_remote_post(
            $endpoint,
            array(
                'headers' => array(
                    'Content-Type'  => 'application/json',
                    'Authorization' => 'Bearer ' . $api_key,
                ),
                'body'       => wp_json_encode( $request_body ),
                'timeout'    => 540,
                'user-agent' => 'WPR-Translate/' . WPR_TRANSLATE_VERSION . '; ' . home_url(),
            )
        );

        if ( is_wp_error( $response ) ) {
            if ( $attempts < $max_attempts ) {
                continue;
            }

            return new WP_Error( 'wpr_translate_openai_request_failed', __( 'Failed to contact the OpenAI translation service.', 'wpr-translate' ) );
        }

        $status_code = (int) wp_remote_retrieve_response_code( $response );

        if ( $status_code < 200 || $status_code >= 300 ) {
            $body_message = wp_remote_retrieve_body( $response );
            $body_message = is_string( $body_message ) ? $body_message : '';

            if ( $attempts < $max_attempts ) {
                continue;
            }

            $error_message = is_string( $body_message ) ? wp_strip_all_tags( $body_message ) : '';

            return new WP_Error(
                'wpr_translate_openai_http_error',
                sprintf(
                    /* translators: 1: HTTP status code. 2: Response body. */
                    __( 'OpenAI translation failed with status %1$d. %2$s', 'wpr-translate' ),
                    $status_code,
                    $error_message
                )
            );
        }

        break;
    }

    $body = wp_remote_retrieve_body( $response );

    if ( '' === $body ) {
        return new WP_Error( 'wpr_translate_openai_empty_response', __( 'OpenAI translation returned an empty response.', 'wpr-translate' ) );
    }

    $decoded_response = json_decode( $body, true );

    if ( null === $decoded_response || ! is_array( $decoded_response ) ) {
        return new WP_Error( 'wpr_translate_openai_invalid_response', __( 'OpenAI translation returned invalid data.', 'wpr-translate' ) );
    }

    $content = '';

    if ( isset( $decoded_response['choices'][0]['message']['content'] ) ) {
        $content = (string) $decoded_response['choices'][0]['message']['content'];
    }

    if ( '' === $content ) {
        return new WP_Error( 'wpr_translate_openai_missing_content', __( 'OpenAI translation response was missing content.', 'wpr-translate' ) );
    }

    $translated_payload = json_decode( $content, true );

    if ( null === $translated_payload || ! is_array( $translated_payload ) ) {
        return new WP_Error( 'wpr_translate_openai_invalid_json', __( 'OpenAI translation did not return valid JSON.', 'wpr-translate' ) );
    }

    return $translated_payload;
}
