#!/usr/bin/env php
<?php
/**
 * Generate .po and .mo files from the POT template.
 *
 * Creates empty .po files for each locale with correct headers,
 * then compiles .mo files using msgfmt.
 *
 * Usage: php generate-po-mo.php
 */

// CLI-only build tool. Guard satisfies Plugin Check; CLI defines ABSPATH below.
if ( ! defined( 'ABSPATH' ) ) {
	define( 'ABSPATH', dirname( __DIR__ ) . '/' );
}

// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped -- CLI tool, not web-facing.

$languages_dir = __DIR__;
$pot_file      = $languages_dir . '/ggm-commerce.pot';
$domain        = 'ggm-commerce';

if (!file_exists($pot_file)) {
    echo "Error: POT file not found at {$pot_file}\n";
    exit(1);
}

// Locale configurations.
$locales = [
    'en_US' => [
        'language'      => 'en_US',
        'language_name' => 'English (United States)',
        'plural_forms'  => 'nplurals=2; plural=(n != 1);',
    ],
    'de_DE' => [
        'language'      => 'de_DE',
        'language_name' => 'German',
        'plural_forms'  => 'nplurals=2; plural=(n != 1);',
    ],
    'fr_FR' => [
        'language'      => 'fr_FR',
        'language_name' => 'French',
        'plural_forms'  => 'nplurals=2; plural=(n > 1);',
    ],
    'es_ES' => [
        'language'      => 'es_ES',
        'language_name' => 'Spanish',
        'plural_forms'  => 'nplurals=2; plural=(n != 1);',
    ],
    'it_IT' => [
        'language'      => 'it_IT',
        'language_name' => 'Italian',
        'plural_forms'  => 'nplurals=2; plural=(n != 1);',
    ],
];

$pot_content = file_get_contents($pot_file);

foreach ($locales as $locale => $config) {
    $po_file = $languages_dir . "/{$domain}-{$locale}.po";

    // Replace header values for this locale.
    $po_content = $pot_content;

    // Update headers.
    $po_content = str_replace(
        '"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"',
        '"PO-Revision-Date: ' . gmdate('Y-m-d H:i:s+00:00') . '\n"',
        $po_content
    );
    $po_content = str_replace(
        '"Language: \n"',
        '"Language: ' . $config['language'] . '\n"',
        $po_content
    );
    $po_content = str_replace(
        '"Language-Team: LANGUAGE <LL@li.org>\n"',
        '"Language-Team: ' . $config['language_name'] . '\n"',
        $po_content
    );
    $po_content = str_replace(
        '"Plural-Forms: nplurals=2; plural=(n != 1);\n"',
        '"Plural-Forms: ' . $config['plural_forms'] . '\n"',
        $po_content
    );
    $po_content = str_replace(
        '"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"',
        '"Last-Translator: \n"',
        $po_content
    );

    file_put_contents($po_file, $po_content);
    echo "Created {$domain}-{$locale}.po ({$config['language_name']})\n";
}

echo "\nNow compiling .mo files...\n";

// Compile .mo files using msgfmt.
$msgfmt_found = false;
$msgfmt_cmd = 'msgfmt';

// Check if msgfmt is available.
exec('which msgfmt 2>/dev/null', $output, $ret);
if ($ret === 0) {
    $msgfmt_found = true;
}

if (!$msgfmt_found) {
    echo "Warning: msgfmt not found. Generating .mo files manually.\n";
    // Fall through to PHP-based .mo generation.
}

foreach ($locales as $locale => $config) {
    $po_file = $languages_dir . "/{$domain}-{$locale}.po";
    $mo_file = $languages_dir . "/{$domain}-{$locale}.mo";

    if ($msgfmt_found) {
        $cmd = sprintf('msgfmt -o %s %s 2>&1', escapeshellarg($mo_file), escapeshellarg($po_file));
        exec($cmd, $cmd_output, $ret);
        if ($ret === 0) {
            echo "Compiled {$domain}-{$locale}.mo\n";
        } else {
            echo "Error compiling {$domain}-{$locale}.mo: " . implode("\n", $cmd_output) . "\n";
        }
    } else {
        // Generate a minimal valid .mo file in PHP.
        generate_mo_file($po_file, $mo_file);
        echo "Generated {$domain}-{$locale}.mo (PHP fallback)\n";
    }
}

echo "\nDone!\n";

/**
 * Generate a .mo file from a .po file using pure PHP.
 * Handles empty translations (untranslated .po files).
 */
function generate_mo_file($po_file, $mo_file) {
    $entries = parse_po_file($po_file);

    // Filter out entries with empty translations — untranslated strings
    // must NOT be in the .mo file so WordPress falls back to the msgid.
    // Only keep the header entry (empty msgid) and entries with actual translations.
    $translated_entries = [];
    foreach ($entries as $entry) {
        if ($entry['msgid'] === '') {
            // Header entry — always include.
            $translated_entries[] = $entry;
        } elseif (!empty($entry['msgstr'])) {
            // Has a translation — include.
            $translated_entries[] = $entry;
        }
        // Empty msgstr with non-empty msgid — skip (untranslated).
    }

    // .mo file format:
    // https://www.gnu.org/software/gettext/manual/html_node/MO-Files.html

    $num_strings = count($translated_entries);

    // Header size: 7 * 4 bytes = 28 bytes.
    $header_size = 28;
    // Original strings table: num_strings * 8 bytes (length + offset pairs).
    $orig_table_offset = $header_size;
    // Translation strings table: num_strings * 8 bytes.
    $trans_table_offset = $orig_table_offset + ($num_strings * 8);
    // Strings start after both tables.
    $strings_offset = $trans_table_offset + ($num_strings * 8);

    // Build string data.
    $originals = [];
    $translations = [];
    $orig_data = '';
    $trans_data = '';
    $orig_offsets = [];
    $trans_offsets = [];

    $current_offset = $strings_offset;

    foreach ($translated_entries as $entry) {
        $orig = $entry['msgid'];
        if (!empty($entry['msgctxt'])) {
            $orig = $entry['msgctxt'] . "\x04" . $orig;
        }
        if (!empty($entry['msgid_plural'])) {
            $orig .= "\x00" . $entry['msgid_plural'];
        }

        $trans = '';
        if (!empty($entry['msgstr'])) {
            if (is_array($entry['msgstr'])) {
                $trans = implode("\x00", $entry['msgstr']);
            } else {
                $trans = $entry['msgstr'];
            }
        }

        $orig_offsets[] = [strlen($orig), $current_offset];
        $current_offset += strlen($orig) + 1; // +1 for null terminator

        $trans_offsets[] = [strlen($trans), $current_offset];
        $current_offset += strlen($trans) + 1;

        $orig_data .= $orig . "\x00";
        $trans_data .= $trans . "\x00";
    }

    // Build the .mo file.
    $mo = '';

    // Magic number (little-endian).
    $mo .= pack('V', 0x950412de);
    // Revision.
    $mo .= pack('V', 0);
    // Number of strings.
    $mo .= pack('V', $num_strings);
    // Offset of original strings table.
    $mo .= pack('V', $orig_table_offset);
    // Offset of translation strings table.
    $mo .= pack('V', $trans_table_offset);
    // Size of hashing table (0 = no hash table).
    $mo .= pack('V', 0);
    // Offset of hashing table.
    $mo .= pack('V', 0);

    // Original strings table.
    foreach ($orig_offsets as $offset) {
        $mo .= pack('V', $offset[0]); // length
        $mo .= pack('V', $offset[1]); // offset
    }

    // Translation strings table.
    foreach ($trans_offsets as $offset) {
        $mo .= pack('V', $offset[0]); // length
        $mo .= pack('V', $offset[1]); // offset
    }

    // String data.
    $mo .= $orig_data;
    $mo .= $trans_data;

    file_put_contents($mo_file, $mo);
}

/**
 * Parse a .po file into entries.
 */
function parse_po_file($po_file) {
    $content = file_get_contents($po_file);
    $lines = explode("\n", $content);
    $entries = [];
    $current = null;
    $current_key = null; // Track which key we're currently reading multi-line values for.

    foreach ($lines as $line) {
        $line = rtrim($line);

        // Skip comments and empty lines between entries.
        if ($line === '' || $line[0] === '#') {
            if ($current !== null && isset($current['msgid'])) {
                $entries[] = $current;
                $current = null;
                $current_key = null;
            }
            continue;
        }

        if (strpos($line, 'msgctxt ') === 0) {
            if ($current !== null && isset($current['msgid'])) {
                $entries[] = $current;
            }
            $current = ['msgctxt' => '', 'msgid' => '', 'msgid_plural' => '', 'msgstr' => ''];
            $current['msgctxt'] = parse_po_string(substr($line, 8));
            $current_key = 'msgctxt';
        } elseif (strpos($line, 'msgid_plural ') === 0) {
            $current['msgid_plural'] = parse_po_string(substr($line, 13));
            $current_key = 'msgid_plural';
        } elseif (strpos($line, 'msgid ') === 0) {
            if ($current !== null && isset($current['msgid']) && $current['msgid'] !== '') {
                $entries[] = $current;
            }
            $current = ['msgctxt' => '', 'msgid' => '', 'msgid_plural' => '', 'msgstr' => ''];
            $current['msgid'] = parse_po_string(substr($line, 6));
            $current_key = 'msgid';
        } elseif (preg_match('/^msgstr\[(\d+)\]\s+/', $line, $m)) {
            $idx = (int) $m[1];
            if (!is_array($current['msgstr'])) {
                $current['msgstr'] = [];
            }
            $current['msgstr'][$idx] = parse_po_string(substr($line, strlen($m[0])));
            $current_key = 'msgstr_' . $idx;
        } elseif (strpos($line, 'msgstr ') === 0) {
            $current['msgstr'] = parse_po_string(substr($line, 7));
            $current_key = 'msgstr';
        } elseif ($line[0] === '"' && $current !== null) {
            // Continuation line.
            $value = parse_po_string($line);
            if ($current_key === 'msgctxt') {
                $current['msgctxt'] .= $value;
            } elseif ($current_key === 'msgid') {
                $current['msgid'] .= $value;
            } elseif ($current_key === 'msgid_plural') {
                $current['msgid_plural'] .= $value;
            } elseif ($current_key === 'msgstr') {
                $current['msgstr'] .= $value;
            } elseif (preg_match('/^msgstr_(\d+)$/', $current_key, $m)) {
                $current['msgstr'][(int) $m[1]] .= $value;
            }
        }
    }

    if ($current !== null && isset($current['msgid'])) {
        $entries[] = $current;
    }

    return $entries;
}

function parse_po_string($str) {
    $str = trim($str);
    if ($str === '""') return '';
    if (strlen($str) >= 2 && $str[0] === '"' && substr($str, -1) === '"') {
        $str = substr($str, 1, -1);
    }
    // Unescape.
    $str = str_replace(['\\n', '\\t', '\\"', '\\\\'], ["\n", "\t", '"', '\\'], $str);
    return $str;
}
