#!/usr/bin/env php
<?php
/**
 * Generate POT file for Goose Commerce plugin.
 *
 * Scans all PHP files and extracts translatable strings using WordPress i18n functions.
 * Outputs a standard .pot file.
 *
 * Usage: php generate-pot.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.

$plugin_dir = dirname(__DIR__);
$domain     = 'ggm-commerce';
$output     = __DIR__ . '/ggm-commerce.pot';

// WordPress i18n functions to scan for.
$functions = [
    // function => [string_arg_positions] (0-indexed)
    '__'              => [0],
    '_e'              => [0],
    'esc_html__'      => [0],
    'esc_html_e'      => [0],
    'esc_attr__'      => [0],
    'esc_attr_e'      => [0],
    '_x'              => [0],  // has context as arg 2
    '_ex'             => [0],  // has context as arg 2
    'esc_html_x'      => [0],
    'esc_attr_x'      => [0],
    '_n'              => [0, 1], // singular, plural
    '_nx'             => [0, 1], // singular, plural (with context)
    '_n_noop'         => [0, 1],
    '_nx_noop'        => [0, 1],
];

// Context argument position for _x family.
$context_functions = [
    '_x'         => 2,
    '_ex'        => 2,
    'esc_html_x' => 2,
    'esc_attr_x' => 2,
    '_nx'        => 4,
    '_nx_noop'   => 3,
];

// Plural functions.
$plural_functions = ['_n', '_nx', '_n_noop', '_nx_noop'];

// Collect all PHP files.
$files = [];
$iterator = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator($plugin_dir, RecursiveDirectoryIterator::SKIP_DOTS)
);

foreach ($iterator as $file) {
    if ($file->getExtension() !== 'php') continue;
    $rel = str_replace($plugin_dir . DIRECTORY_SEPARATOR, '', $file->getPathname());
    $rel = str_replace('\\', '/', $rel);
    // Skip vendor, node_modules, .git, languages dir itself, test/docs.
    if (preg_match('#^(vendor|node_modules|\.git|languages|docs|config|src)/#', $rel)) continue;
    $files[] = $file->getPathname();
}

sort($files);

// Extract strings.
$entries = []; // key => ['singular'=>..., 'plural'=>..., 'context'=>..., 'references'=>[], 'comments'=>[]]

foreach ($files as $filepath) {
    $content = file_get_contents($filepath);
    $rel_path = str_replace($plugin_dir . DIRECTORY_SEPARATOR, '', $filepath);
    $rel_path = str_replace('\\', '/', $rel_path);
    $lines = explode("\n", $content);

    foreach ($functions as $func => $arg_positions) {
        // Build regex to find function calls.
        $pattern = '/\b' . preg_quote($func, '/') . '\s*\(/';
        $offset = 0;

        while (preg_match($pattern, $content, $m, PREG_OFFSET_CAPTURE, $offset)) {
            $match_pos = $m[0][1];
            $offset = $match_pos + 1;

            // Find the line number.
            $line_num = substr_count($content, "\n", 0, $match_pos) + 1;

            // Extract arguments by finding matching parenthesis.
            $paren_start = $match_pos + strlen($m[0][0]) - 1; // position of opening (
            $args = extract_arguments($content, $paren_start);

            if ($args === null) continue;

            // Check domain - last arg should be the domain.
            $last_arg = trim(end($args));
            if (!is_string_literal($last_arg)) continue;
            $arg_domain = extract_string_value($last_arg);
            if ($arg_domain !== $domain) continue;

            // Extract strings from specified positions.
            $singular = null;
            $plural = null;
            $context = null;

            foreach ($arg_positions as $pos) {
                if (!isset($args[$pos])) continue;
                $arg = trim($args[$pos]);
                if (!is_string_literal($arg)) continue;
                $str = extract_string_value($arg);

                if ($pos === 0) {
                    $singular = $str;
                } elseif ($pos === 1 && in_array($func, $plural_functions)) {
                    $plural = $str;
                }
            }

            // Extract context if applicable.
            if (isset($context_functions[$func])) {
                $ctx_pos = $context_functions[$func];
                if (isset($args[$ctx_pos])) {
                    $ctx_arg = trim($args[$ctx_pos]);
                    if (is_string_literal($ctx_arg)) {
                        $context = extract_string_value($ctx_arg);
                    }
                }
            }

            if ($singular === null) continue;

            // Look for translators comment above.
            $translator_comment = find_translators_comment($lines, $line_num - 1);

            // Build entry key.
            $key = ($context ? $context . "\x04" : '') . $singular . ($plural ? "\x00" . $plural : '');

            if (!isset($entries[$key])) {
                $entries[$key] = [
                    'singular'   => $singular,
                    'plural'     => $plural,
                    'context'    => $context,
                    'references' => [],
                    'comments'   => [],
                ];
            }

            $entries[$key]['references'][] = $rel_path . ':' . $line_num;

            if ($translator_comment && !in_array($translator_comment, $entries[$key]['comments'])) {
                $entries[$key]['comments'][] = $translator_comment;
            }
        }
    }
}

// Sort entries by first reference.
uasort($entries, function ($a, $b) {
    return strcmp($a['references'][0] ?? '', $b['references'][0] ?? '');
});

// Generate POT file.
$pot = [];
$pot[] = '# Copyright (C) 2025 Green Goose Manor - Giles Beckley';
$pot[] = '# This file is distributed under the GPL v2 or later.';
$pot[] = 'msgid ""';
$pot[] = 'msgstr ""';
$pot[] = '"Project-Id-Version: Goose Commerce 1.0.0\n"';
$pot[] = '"Report-Msgid-Bugs-To: https://github.com/Giles-Beckley/ggm-commerce/issues\n"';
$pot[] = '"POT-Creation-Date: ' . gmdate('Y-m-d H:i:s+00:00') . '\n"';
$pot[] = '"MIME-Version: 1.0\n"';
$pot[] = '"Content-Type: text/plain; charset=UTF-8\n"';
$pot[] = '"Content-Transfer-Encoding: 8bit\n"';
$pot[] = '"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"';
$pot[] = '"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"';
$pot[] = '"Language-Team: LANGUAGE <LL@li.org>\n"';
$pot[] = '"Language: \n"';
$pot[] = '"Plural-Forms: nplurals=2; plural=(n != 1);\n"';
$pot[] = '"X-Generator: Goose Commerce POT Generator\n"';
$pot[] = '"X-Domain: ggm-commerce\n"';
$pot[] = '';

foreach ($entries as $entry) {
    // Translator comments.
    foreach ($entry['comments'] as $comment) {
        $pot[] = '#. ' . $comment;
    }

    // References.
    foreach ($entry['references'] as $ref) {
        $pot[] = '#: ' . $ref;
    }

    // Context.
    if ($entry['context']) {
        $pot[] = 'msgctxt ' . pot_escape($entry['context']);
    }

    // Singular.
    $pot[] = 'msgid ' . pot_escape($entry['singular']);

    // Plural.
    if ($entry['plural'] !== null) {
        $pot[] = 'msgid_plural ' . pot_escape($entry['plural']);
        $pot[] = 'msgstr[0] ""';
        $pot[] = 'msgstr[1] ""';
    } else {
        $pot[] = 'msgstr ""';
    }

    $pot[] = '';
}

file_put_contents($output, implode("\n", $pot));

$count = count($entries);
echo "Generated {$output}\n";
echo "Extracted {$count} translatable strings from " . count($files) . " PHP files.\n";

// ---- Helper functions ----

function extract_arguments($content, $paren_pos) {
    $len = strlen($content);
    $depth = 0;
    $args = [];
    $current = '';
    $i = $paren_pos;
    $in_string = false;
    $string_char = '';
    $escape_next = false;

    for (; $i < $len; $i++) {
        $ch = $content[$i];

        if ($escape_next) {
            $current .= $ch;
            $escape_next = false;
            continue;
        }

        if ($ch === '\\' && $in_string) {
            $current .= $ch;
            $escape_next = true;
            continue;
        }

        if ($in_string) {
            $current .= $ch;
            if ($ch === $string_char) {
                $in_string = false;
            }
            continue;
        }

        if ($ch === '"' || $ch === "'") {
            $current .= $ch;
            $in_string = true;
            $string_char = $ch;
            continue;
        }

        if ($ch === '(') {
            $depth++;
            if ($depth === 1) continue; // skip opening paren
            $current .= $ch;
            continue;
        }

        if ($ch === ')') {
            $depth--;
            if ($depth === 0) {
                $args[] = $current;
                return $args;
            }
            $current .= $ch;
            continue;
        }

        if ($ch === ',' && $depth === 1) {
            $args[] = $current;
            $current = '';
            continue;
        }

        $current .= $ch;
    }

    return null; // unmatched parens
}

function is_string_literal($arg) {
    $arg = trim($arg);
    return (
        (strlen($arg) >= 2 && $arg[0] === "'" && substr($arg, -1) === "'") ||
        (strlen($arg) >= 2 && $arg[0] === '"' && substr($arg, -1) === '"')
    );
}

function extract_string_value($arg) {
    $arg = trim($arg);
    $quote = $arg[0];
    $inner = substr($arg, 1, -1);

    if ($quote === '"') {
        // Handle double-quoted string escapes.
        $inner = str_replace(['\\n', '\\t', '\\"', '\\\\', '\\$'], ["\n", "\t", '"', '\\', '$'], $inner);
    } else {
        // Single-quoted: only \\ and \' are escapes.
        $inner = str_replace(["\\'", '\\\\'], ["'", '\\'], $inner);
    }

    return $inner;
}

function find_translators_comment($lines, $target_line_index) {
    // Search up to 5 lines above for a translators comment.
    for ($i = $target_line_index; $i >= max(0, $target_line_index - 5); $i--) {
        $line = trim($lines[$i]);
        if (preg_match('#/\*\s*translators:\s*(.+?)\s*\*/#i', $line, $m)) {
            return trim($m[1]);
        }
    }
    return null;
}

function pot_escape($string) {
    $string = str_replace('\\', '\\\\', $string);
    $string = str_replace('"', '\\"', $string);

    // Handle multiline strings.
    if (strpos($string, "\n") !== false) {
        $parts = explode("\n", $string);
        $result = '""' . "\n";
        foreach ($parts as $i => $part) {
            $suffix = ($i < count($parts) - 1) ? '\\n' : '';
            $result .= '"' . $part . $suffix . '"' . "\n";
        }
        return rtrim($result);
    }

    return '"' . $string . '"';
}
