<?php
declare(strict_types=1);

namespace Celena\Core\Template\Tags;

/**
 * Встроенный блочный тег: [foreach var="items" as="item"]...[/foreach]
 *
 *  - var  — имя переменной с массивом во внешнем $vars (поддерживает dot-нотацию)
 *  - as   — имя переменной для текущего элемента (по умолчанию "item")
 *
 * В теле доступны: {item}, {item.title} и т.д., а также служебные:
 *   {_index} — 0-based индекс
 *   {_total} — общее количество
 *   {_first}, {_last} — "1" если первый/последний, иначе ""
 *
 * Если массив пустой — тело не рендерится. Для пустого состояния используйте
 * соседний [if {var_empty}="1"] и передайте флаг из контроллера.
 */
final class ForeachTag
{
    public function __invoke(array $params, array $vars, callable $renderBody): string
    {
        $varName = (string) ($params['var'] ?? '');
        $alias   = (string) ($params['as'] ?? 'item');
        if ($varName === '') {
            return '';
        }
        $list = $this->resolve($vars, $varName);
        if (!is_iterable($list)) {
            return '';
        }
        $items = is_array($list) ? array_values($list) : iterator_to_array($list, false);
        $total = count($items);
        if ($total === 0) {
            return '';
        }
        $out = '';
        foreach ($items as $i => $item) {
            $loopVars = $vars;
            $loopVars[$alias] = $item;
            $loopVars['_index'] = $i;
            $loopVars['_total'] = $total;
            $loopVars['_first'] = $i === 0 ? '1' : '';
            $loopVars['_last']  = $i === $total - 1 ? '1' : '';
            $out .= (string) $renderBody($loopVars);
        }
        return $out;
    }

    private function resolve(array $vars, string $name): mixed
    {
        if (!str_contains($name, '.')) {
            return $vars[$name] ?? null;
        }
        $current = $vars;
        foreach (explode('.', $name) as $seg) {
            if (is_array($current) && array_key_exists($seg, $current)) {
                $current = $current[$seg];
            } elseif (is_object($current) && isset($current->$seg)) {
                $current = $current->$seg;
            } else {
                return null;
            }
        }
        return $current;
    }
}
