<?php
/**
 * Helpers for exporting stored translations into gettext files.
 *
 * @package WPR_Translate
 */

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

require_once WPR_TRANSLATE_PATH . 'includes/translation-files.php';
require_once WPR_TRANSLATE_PATH . 'includes/admin/string-targets.php';
require_once WPR_TRANSLATE_PATH . 'includes/admin/string-database.php';

/**
 * Extract a slug from the stored context identifier.
 *
 * @param string $context Context identifier.
 *
 * @return string
 */
function wpr_translate_admin_extract_slug_from_context( $context ) {
    // Normalize the incoming context string.
    $context = (string) $context;

    if ( 0 === strpos( $context, 'theme:' ) ) {
        $context = substr( $context, strlen( 'theme:' ) );
    } elseif ( 0 === strpos( $context, 'plugin:' ) ) {
        $context = substr( $context, strlen( 'plugin:' ) );
    }

    // Return a normalized text domain slug.
    return wpr_translate_normalize_translation_domain( $context );
}

/**
 * Determine the locale value for a language code.
 *
 * @param array  $languages Configured languages.
 * @param string $code      Language code.
 *
 * @return string
 */
function wpr_translate_admin_get_locale_for_language( $languages, $code ) {
    // Return early when inputs are missing.
    if ( empty( $languages ) || '' === $code ) {
        return '';
    }

    foreach ( $languages as $language ) {
        if ( ! isset( $language['code'] ) ) {
            continue;
        }

        if ( strtolower( (string) $language['code'] ) === strtolower( (string) $code ) ) {
            if ( isset( $language['locale'] ) && '' !== $language['locale'] ) {
                return (string) $language['locale'];
            }

            return (string) $language['code'];
        }
    }

    // Fallback to the original code when no locale is available.
    return (string) $code;
}

/**
 * Generate MO files for newly translated strings.
 *
 * @return array|WP_Error
 */
function wpr_translate_admin_generate_translation_files() {
    // Load configured languages for export.
    $languages = get_option( 'wpr_translate_languages', array() );

    if ( empty( $languages ) ) {
        return new WP_Error( 'wpr_translate_no_languages', __( 'Add at least one language before generating files.', 'wpr-translate' ) );
    }

    if ( ! function_exists( 'wpr_translate_admin_get_default_language_code' ) ) {
        return new WP_Error( 'wpr_translate_missing_helper', __( 'Default language helper is unavailable.', 'wpr-translate' ) );
    }

    // Resolve the default language for export decisions.
    $default_code = wpr_translate_admin_get_default_language_code( $languages );

    global $wpdb;

    // Confirm the strings table is available before querying.
    $table     = $wpdb->prefix . 'wpestate_translation_strings';
    $has_table = ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) ) === $table );

    if ( ! $has_table ) {
        return new WP_Error( 'wpr_translate_strings_table_missing', __( 'The translation strings table could not be found.', 'wpr-translate' ) );
    }

    // Ensure the processed flag exists for export tracking.
    $has_processed_flag = wpr_translate_admin_ensure_processed_column();

    if ( ! $has_processed_flag ) {
        return new WP_Error( 'wpr_translate_processed_flag_missing', __( 'Could not prepare the processed flag for translations.', 'wpr-translate' ) );
    }

    // Export all stored rows so generated files always reflect the current DB state.
    $sql  = "SELECT string_id, context, value, language_code, translation FROM {$table}";
    $rows = $wpdb->get_results( $sql, ARRAY_A );

    if ( empty( $rows ) ) {
        return array(
            'files'   => 0,
            'strings' => 0,
            'errors'  => array(),
        );
    }

    // Group translations by context and language code.
    $grouped = array();

    foreach ( $rows as $row ) {
        $translation = isset( $row['translation'] ) ? (string) $row['translation'] : '';

        if ( '' === trim( wp_strip_all_tags( $translation ) ) ) {
            continue;
        }

        $context = isset( $row['context'] ) ? (string) $row['context'] : '';
        $code    = isset( $row['language_code'] ) ? (string) $row['language_code'] : '';

        if ( '' === $context || '' === $code ) {
            continue;
        }

        if ( ! isset( $grouped[ $context ] ) ) {
            $grouped[ $context ] = array();
        }

        if ( ! isset( $grouped[ $context ][ $code ] ) ) {
            $grouped[ $context ][ $code ] = array();
        }

        $grouped[ $context ][ $code ][] = array(
            'id'          => isset( $row['string_id'] ) ? (int) $row['string_id'] : 0,
            'original'    => isset( $row['value'] ) ? (string) $row['value'] : '',
            'translation' => $translation,
        );
    }

    if ( empty( $grouped ) ) {
        return array(
            'files'   => 0,
            'strings' => 0,
            'errors'  => array(),
        );
    }

    // Ensure the custom translations directory exists.
    $directory = wpr_translate_prepare_custom_translations_directory();

    if ( is_wp_error( $directory ) ) {
        return $directory;
    }

    // Resolve scan targets to map contexts to domains.
    $targets = function_exists( 'wpr_translate_admin_get_scan_targets' )
        ? wpr_translate_admin_get_scan_targets()
        : array();
    $domains = array();

    foreach ( $targets as $target ) {
        $context = isset( $target['context'] ) ? $target['context'] : '';
        $domain  = isset( $target['domain'] ) ? $target['domain'] : '';

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

        if ( '' === $domain ) {
            $domain = wpr_translate_admin_extract_slug_from_context( $context );
        }

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

        $domains[ $context ] = $domain;
    }

    // Load WordPress POMO classes when needed.
    if ( ! class_exists( 'MO' ) ) {
        require_once ABSPATH . WPINC . '/pomo/mo.php';
    }

    if ( ! class_exists( 'Translation_Entry' ) ) {
        require_once ABSPATH . WPINC . '/pomo/entry.php';
    }

    // Track export results for reporting.
    $files_created = 0;
    $processed_ids = array();
    $errors        = array();

    foreach ( $grouped as $context => $languages_group ) {
        // Resolve the domain slug for each context.
        if ( ! isset( $domains[ $context ] ) ) {
            $errors[] = sprintf( __( 'Unable to determine a text domain for %s.', 'wpr-translate' ), $context );
            continue;
        }

        $domain = $domains[ $context ];
        $slug   = wpr_translate_normalize_translation_domain( $domain );

        if ( '' === $slug ) {
            $errors[] = sprintf( __( 'Incomplete export configuration for %s.', 'wpr-translate' ), $context );
            continue;
        }

        foreach ( $languages_group as $code => $entries ) {
            if ( empty( $entries ) ) {
                continue;
            }

            // Resolve locale and import existing MO if present.
            $locale = wpr_translate_admin_get_locale_for_language( $languages, $code );

            if ( '' === $locale ) {
                $locale = $code;
            }

            // Build the MO file contents in memory.
            $mo = new MO();

            $existing_mofile = wpr_translate_locate_custom_mofile( $domain, $locale );

            if ( '' !== $existing_mofile ) {
                $mo->import_from_file( $existing_mofile );
            }

            $mo->set_header( 'Project-Id-Version', 'WPResidence Translate' );
            $mo->set_header( 'Report-Msgid-Bugs-To', '' );
            $mo->set_header( 'POT-Creation-Date', gmdate( 'Y-m-d H:i:s+0000' ) );
            $mo->set_header( 'PO-Revision-Date', gmdate( 'Y-m-d H:i:s+0000' ) );
            $mo->set_header( 'Last-Translator', get_bloginfo( 'name' ) );
            $mo->set_header( 'Language-Team', get_bloginfo( 'name' ) );
            $mo->set_header( 'Language', $locale );
            $mo->set_header( 'MIME-Version', '1.0' );
            $mo->set_header( 'Content-Type', 'text/plain; charset=UTF-8' );
            $mo->set_header( 'Content-Transfer-Encoding', '8bit' );
            $mo->set_header( 'Plural-Forms', 'nplurals=2; plural=(n != 1);' );

            // Add each translated entry and track IDs for marking processed.
            $entry_ids = array();

            foreach ( $entries as $entry_data ) {
                $original    = isset( $entry_data['original'] ) ? (string) $entry_data['original'] : '';
                $translation = isset( $entry_data['translation'] ) ? (string) $entry_data['translation'] : '';

                if ( '' === $original || '' === $translation ) {
                    continue;
                }

                $entry = new Translation_Entry(
                    array(
                        'singular'     => $original,
                        'translations' => array( $translation ),
                    )
                );

                $mo->add_entry( $entry );

                if ( isset( $entry_data['id'] ) ) {
                    $entry_ids[] = (int) $entry_data['id'];
                }
            }

            if ( empty( $entry_ids ) ) {
                continue;
            }

            // Determine the export path for this locale.
            $file_path = wpr_translate_build_custom_mofile_path( $domain, $locale );

            if ( '' === $file_path ) {
                $errors[] = sprintf( __( 'Failed to determine a file path for %1$s (%2$s).', 'wpr-translate' ), $slug, $locale );
                continue;
            }

            // Write the MO file to disk.
            if ( ! $mo->export_to_file( $file_path ) ) {
                $errors[] = sprintf( __( 'Failed to write %s.', 'wpr-translate' ), $file_path );
                continue;
            }

            $files_created++;
            $processed_ids = array_merge( $processed_ids, $entry_ids );

        }
    }

    // Mark exported rows as processed to avoid re-exporting.
    $processed_ids = array_unique( array_filter( $processed_ids ) );

    if ( ! empty( $processed_ids ) ) {
        $placeholders = implode( ',', array_fill( 0, count( $processed_ids ), '%d' ) );
        $update_sql   = "UPDATE {$table} SET processed = 1 WHERE string_id IN ({$placeholders})";
        $params       = $processed_ids;
        array_unshift( $params, $update_sql );

        $wpdb->query( call_user_func_array( array( $wpdb, 'prepare' ), $params ) );
    }

    return array(
        'files'   => $files_created,
        'strings' => count( $processed_ids ),
        'errors'  => $errors,
    );
}
