<?php

namespace WPSN\SEO;

defined('ABSPATH') || exit;

/**
 * Helper for parsing TemplateEngine token expressions.
 *
 * Syntax examples:
 * - %title%                     (simple)
 * - %title??sitename%           (fallback)
 * - %excerpt|strip|truncate(160)%
 * - %acf:brand|lower??sitename%
 */
final class TemplateExpression
{
    /**
     * Split a string by a delimiter, but only when outside quotes and outside parentheses.
     *
     * This is used to safely split by:
     * - fallback operator: ??
     * - transform pipeline: |
     *
     * @return string[]
     */
    public static function splitOutside(string $input, string $delimiter): array
    {
        if ($delimiter === '') {
            return [$input];
        }

        $len = strlen($input);
        $dlen = strlen($delimiter);

        if ($len === 0 || $dlen === 0) {
            return [$input];
        }

        $parts = [];
        $buf = '';
        $depth = 0;
        $quote = null; // ' or "

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

            // Inside quotes.
            if ($quote !== null) {
                if ($ch === '\\' && ($i + 1) < $len) {
                    // Preserve escape sequence.
                    $buf .= $ch . $input[$i + 1];
                    $i++;
                    continue;
                }
                if ($ch === $quote) {
                    $quote = null;
                }
                $buf .= $ch;
                continue;
            }

            // Enter quote.
            if ($ch === '"' || $ch === "'") {
                $quote = $ch;
                $buf .= $ch;
                continue;
            }

            // Parentheses depth.
            if ($ch === '(') {
                $depth++;
                $buf .= $ch;
                continue;
            }
            if ($ch === ')' && $depth > 0) {
                $depth--;
                $buf .= $ch;
                continue;
            }

            // Split only when outside parentheses.
            if ($depth === 0 && substr($input, $i, $dlen) === $delimiter) {
                $parts[] = $buf;
                $buf = '';
                $i += ($dlen - 1);
                continue;
            }

            $buf .= $ch;
        }

        $parts[] = $buf;
        return $parts;
    }

    /**
     * Parse a transform part.
     *
     * Supported forms:
     * - lower
     * - truncate(160)
     * - truncate(160,"…")
     * - prefix("Buy ")
     * - suffix(" | %sitename%")
     * - truncate:160 (legacy convenience)
     *
     * @return array{name:string,args:string[]}
     */
    public static function parseTransform(string $part): array
    {
        $part = trim($part);
        if ($part === '') {
            return ['name' => '', 'args' => []];
        }

        // convenience: name:arg
        if (strpos($part, '(') === false && strpos($part, ':') !== false) {
            [$n, $rest] = explode(':', $part, 2);
            $n = trim($n);
            $rest = trim($rest);
            if ($n !== '') {
                return ['name' => $n, 'args' => [$rest]];
            }
        }

        if (preg_match('/^([a-zA-Z0-9_\-]+)\((.*)\)$/us', $part, $m)) {
            $name = trim((string) ($m[1] ?? ''));
            $argsStr = (string) ($m[2] ?? '');
            return ['name' => $name, 'args' => self::parseArgs($argsStr)];
        }

        return ['name' => $part, 'args' => []];
    }

    /**
     * @return string[]
     */
    private static function parseArgs(string $argsStr): array
    {
        $argsStr = trim($argsStr);
        if ($argsStr === '') {
            return [];
        }

        $raw = self::splitOutside($argsStr, ',');
        $out = [];
        foreach ($raw as $r) {
            $r = trim($r);
            if ($r === '') {
                continue;
            }
            $out[] = self::unquote($r);
        }
        return $out;
    }

    private static function unquote(string $s): string
    {
        $s = trim($s);
        if ($s === '') {
            return '';
        }

        $first = $s[0];
        $last = substr($s, -1);
        if (($first === '"' && $last === '"') || ($first === "'" && $last === "'")) {
            $inner = substr($s, 1, -1);
            // Unescape common sequences.
            $inner = str_replace(['\\"', "\\'", '\\\\'], ['"', "'", '\\'], $inner);
            return $inner;
        }

        return $s;
    }
}
