<?php

namespace Storeplugin\PDFInvoice\Framework\Fields\Types;

defined('ABSPATH') || exit;

use Storeplugin\PDFInvoice\Framework\Abstracts\AbstractField;
use Storeplugin\PDFInvoice\Framework\Fields\FieldRenderer;
use Storeplugin\PDFInvoice\Framework\Utils\FieldState;
use Storeplugin\PDFInvoice\Asset;

class RepeaterField extends AbstractField
{
    private static bool $framework_assets_registered = false;
    private static bool $sortable_enqueued = false;
    private static int $renderDepth = 0;
    public const ROW_KEY_FIELD = '__nxtf_row_key';

    /**
     * Get the field type.
     *
     * @return string
     */
    public static function getType(): string
    {
        return 'repeater';
    }

    /**
     * Render the repeater field.
     *
     * @param array  $field  Field definition (includes 'fields' sub-field defs).
     * @param mixed  $value  Array of row data.
     * @param string $prefix Name prefix.
     *
     * @return string
     */
    public function render(array $field, $value, string $prefix): string
    {
        $id = $field['id'];
        $subFields = isset($field['fields']) && is_array($field['fields']) ? $field['fields'] : [];
        $isDisabled = $this->hasTruthyFlag($field, 'disabled');
        $isReadonly = !$isDisabled && $this->hasTruthyFlag($field, 'readonly');
        $isLocked = $isDisabled || $isReadonly;

        if (empty($subFields)) {
            return '';
        }

        $this->enqueueAssets();

        $depth = self::$renderDepth;
        self::$renderDepth++;

        $rows = is_array($value) ? array_values($value) : [];
        if (empty($rows)) {
            $rows = [[]];
        }
        $buttonLabel = isset($field['button_label']) && is_string($field['button_label']) && $field['button_label'] !== ''
            ? $field['button_label']
            : 'Add Row';
        $maxRows = isset($field['max_rows']) ? (int) $field['max_rows'] : 0;
        $blockLabelField = isset($field['block_label'])
            ? \sanitize_key((string) $field['block_label'])
            : '';
        $repeaterPrefix = sprintf('%s[%s]', $prefix, $id);
        $ancestorRenderPrefix = isset($field['_render_prefix']) && is_string($field['_render_prefix'])
            ? $field['_render_prefix']
            : '';

        $rawLayout = $field['layout'] ?? 'vertical';
        $layout = in_array($rawLayout, ['horizontal', 'vertical'], true) ? $rawLayout : 'vertical';
        $rawColSpan = $field['default_col_span'] ?? 6;
        $defaultColSpan = is_numeric($rawColSpan) ? max(1, min(12, (int) $rawColSpan)) : 6;
        $placeholder = sprintf('__INDEX_%d__', $depth);
        $rowKeyPlaceholder = sprintf('__ROW_KEY_%d__', $depth);
        $baseRowContext = [
            'repeater_prefix' => $repeaterPrefix,
            'block_label_field' => $blockLabelField,
            'layout' => $layout,
            'default_col_span' => $defaultColSpan,
            'is_disabled' => $isDisabled,
            'is_readonly' => $isReadonly,
            'nxtf_ancestor_render_prefix' => $ancestorRenderPrefix,
        ];

        try {
            $templateHtml = $this->renderRow($subFields, [], array_merge($baseRowContext, [
                'index' => $placeholder,
                'row_key' => $rowKeyPlaceholder,
            ]));

            $containerAttrs = $this->buildAttributes([
                'data-repeater' => $id,
                'data-repeater-prefix' => $repeaterPrefix,
                'data-repeater-max' => $maxRows > 0 ? (string) $maxRows : null,
                'data-repeater-block-label' => $blockLabelField !== '' ? $blockLabelField : null,
                'data-repeater-next-index' => (string) count($rows),
                'data-repeater-depth' => (string) $depth,
            ]);

            $containerClass = $this->getContainerClasses();
            if ($isLocked) {
                $containerClass .= ' nxtf-repeater-locked';
            }
            if ($isDisabled) {
                $containerClass .= ' nxtf-repeater-disabled';
            }
            if ($isReadonly) {
                $containerClass .= ' nxtf-repeater-readonly';
            }

            $html = sprintf('<div class="%s" %s>', \esc_attr($containerClass), $containerAttrs);

            // Row template (inert — not rendered, doesn't interfere with form submission)
            $html .= sprintf('<template data-repeater-tpl>%s</template>', $templateHtml);

            // Rows container
            $html .= '<div data-repeater-rows>';
            foreach ($rows as $index => $rowValue) {
                $rowKey = self::buildRowKey($rowValue, (string) $index);
                $html .= $this->renderRow($subFields, $rowValue, array_merge($baseRowContext, [
                    'index' => (int) $index,
                    'row_key' => $rowKey,
                ]));
            }
            $html .= '</div>';

            // Add button
            $addBtnDisabled = $isLocked || ($maxRows > 0 && count($rows) >= $maxRows);
            $addBtnAttrs = $this->buildAttributes([
                'type' => 'button',
                'data-repeater-add' => '1',
                'class' => $this->getAddButtonClasses(),
                'disabled' => $addBtnDisabled ?: null,
            ]);
            $html .= sprintf(
                '<button %s><span class="dashicons dashicons-plus-alt2" style="font-size:16px;width:16px;height:16px;line-height:16px;"></span> %s</button>',
                $addBtnAttrs,
                \esc_html($buttonLabel)
            );

            $html .= '</div>';

            return $html;
        } finally {
            self::$renderDepth--;
        }
    }

    /**
     * Render a single repeater row.
     *
     * @param array $subFields Sub-field definitions.
     * @param mixed $rowValue  Row data (associative array keyed by sub-field id).
     * @param array{
     *     repeater_prefix:string,
     *     index:int|string,
     *     block_label_field:string,
     *     layout:string,
     *     default_col_span:int,
     *     row_key?:string,
     *     is_disabled?:bool,
     *     is_readonly?:bool,
     *     nxtf_ancestor_render_prefix?:string
     * } $context Row render context.
     *
     * @return string
     */
    private function renderRow(array $subFields, $rowValue, array $context): string
    {
        $repeaterPrefix = (string) ($context['repeater_prefix'] ?? '');
        $index = $context['index'] ?? 0;
        $blockLabelField = (string) ($context['block_label_field'] ?? '');
        $layout = (string) ($context['layout'] ?? 'vertical');
        if (!in_array($layout, ['horizontal', 'vertical'], true)) {
            $layout = 'vertical';
        }

        $defaultColSpan = (int) ($context['default_col_span'] ?? 6);
        $rowKey = isset($context['row_key']) && is_string($context['row_key']) ? $context['row_key'] : '';
        $isDisabled = !empty($context['is_disabled']);
        $isReadonly = !empty($context['is_readonly']);
        $ancestorRenderPrefix = isset($context['nxtf_ancestor_render_prefix']) && is_string($context['nxtf_ancestor_render_prefix'])
            ? $context['nxtf_ancestor_render_prefix']
            : '';
        $isLocked = $isDisabled || $isReadonly;
        $rowPrefix = sprintf('%s[%s]', $repeaterPrefix, $index);
        $rowValue = is_array($rowValue) ? $rowValue : [];
        if ($rowKey === '') {
            $rowKey = self::buildRowKey($rowValue, (string) $index);
        }

        $html = sprintf(
            '<div data-repeater-row data-repeater-index="%s" class="%s">',
            \esc_attr((string) $index),
            \esc_attr($this->getRowClasses())
        );

        $html .= sprintf(
            '<input %s>',
            $this->buildAttributes([
                'type' => 'hidden',
                'name' => sprintf('%s[%s]', $rowPrefix, self::ROW_KEY_FIELD),
                'value' => $rowKey,
            ])
        );

        // Row header
        $html .= sprintf('<div data-repeater-header class="%s">', \esc_attr($this->getRowHeaderClasses()));

        $html .= '<div style="display:flex;align-items:center;gap:8px;">';
        $html .= sprintf(
            '<span data-repeater-handle class="dashicons dashicons-menu" style="%s" title="Drag to reorder"></span>',
            \esc_attr($isLocked
                ? 'cursor:default;color:#cbd5e1;font-size:16px;width:16px;height:16px;line-height:16px;'
                : 'cursor:grab;color:#94a3b8;font-size:16px;width:16px;height:16px;line-height:16px;')
        );

        if ($blockLabelField !== '') {
            $rawLabel = $rowValue[$blockLabelField] ?? null;
            $labelText = ($rawLabel !== null && $rawLabel !== '' && (is_string($rawLabel) || is_numeric($rawLabel)))
                ? (string) $rawLabel
                : '';
            $html .= sprintf(
                '<span data-repeater-label style="font-size:13px;font-weight:500;color:#64748b;">%s</span>',
                \esc_html($labelText)
            );
        }

        $html .= '</div>';

        $html .= '<div class="nxtf-repeater-header-item-btn-group">';
        $html .= sprintf(
            '<button %s><span class="dashicons dashicons-trash" style="font-size:16px;width:16px;height:16px;line-height:16px;"></span></button>',
            $this->buildAttributes([
                'type' => 'button',
                'data-repeater-remove' => '1',
                'class' => 'nxtf-repeater-delete-btn',
                'title' => 'Remove row',
                'disabled' => $isLocked ? true : null,
            ])
        );
        $html .= sprintf(
            '<button type="button" data-repeater-toggle class="%s" title="%s"><span class="dashicons dashicons-arrow-up-alt2" style="font-size:16px;width:16px;height:16px;line-height:16px;transition:transform 0.2s;"></span></button>',
            \esc_attr('nxtf-repeater-collapsed-btn'),
            \esc_attr('Toggle fields')
        );
        $html .= '</div>';

        $html .= '</div>';

        // Sub-fields
        $html .= sprintf('<div data-repeater-fields class="%s">', \esc_attr($this->getFieldsContainerClasses($layout)));

        foreach ($subFields as $subField) {
            if (!is_array($subField)) {
                continue;
            }

            $subId = isset($subField['id']) ? \sanitize_key((string) $subField['id']) : '';
            if ($subId === '') {
                continue;
            }

            // Inject col_span for horizontal layout when not explicitly set
            if ($layout === 'horizontal' && !isset($subField['col_span'])) {
                $subField['col_span'] = $defaultColSpan;
            }

            $subField = $this->applyLockStateToSubField($subField, $isDisabled, $isReadonly);

            $subValue = $rowValue[$subId] ?? null;
            if ($ancestorRenderPrefix !== '') {
                $subField['_nxtf_ancestor_render_prefix'] = $ancestorRenderPrefix;
            }

            $html .= FieldRenderer::renderSubField($subField, $subValue, $rowPrefix);
        }

        $html .= '</div>';
        $html .= '</div>';

        return $html;
    }

    /**
     * Build a stable row key for matching submitted rows to their pre-save snapshot.
     *
     * @param mixed  $rowValue
     * @param string $index
     * @return string
     */
    public static function buildRowKey($rowValue, string $index): string
    {
        $normalizedRow = is_array($rowValue) ? $rowValue : [];
        unset($normalizedRow[self::ROW_KEY_FIELD]);

        $seed = \wp_json_encode([
            'index' => $index,
            'row' => $normalizedRow,
        ]);

        if (!is_string($seed) || $seed === '') {
            $seed = serialize([$index, $normalizedRow]);
        }

        return 'row_' . substr(md5($seed), 0, 16);
    }

    /**
     * Enqueue assets needed by the repeater.
     */
    private function enqueueAssets(): void
    {
        if (!self::$framework_assets_registered) {
            Asset::registerFrameworkAssets();
            self::$framework_assets_registered = true;
        }

        if (!self::$sortable_enqueued) {
            \wp_enqueue_script('sortablejs');
            self::$sortable_enqueued = true;
        }
    }

    /**
     * Propagate repeater lock state to a sub-field without collapsing readonly into disabled.
     *
     * @param array $subField
     * @param bool  $isDisabled
     * @param bool  $isReadonly
     * @return array
     */
    private function applyLockStateToSubField(array $subField, bool $isDisabled, bool $isReadonly): array
    {
        return FieldState::applyRepeaterLockToSubField($subField, $isDisabled, $isReadonly);
    }

    /**
     * Whether a sub-field can honor repeater readonly state without being disabled.
     *
     * @param string $type
     * @return bool
     */
    private function supportsReadonlyLock(string $type): bool
    {
        return FieldState::isReadonlyLockable($type);
    }

    private function getContainerClasses(): string
    {
        return 'nxtf-repeater-container';
    }

    private function getRowClasses(): string
    {
        return 'nxtf-repeater-item';
    }

    private function getRowHeaderClasses(): string
    {
        return 'nxtf-repeater-item-header';
    }

    private function getFieldsContainerClasses(string $layout = 'vertical'): string
    {
        if ($layout === 'horizontal') {
            return 'nxtf-repeater-horizontal-body-item';
        }

        return 'nxtf-repeater-body-item';
    }

    private function getAddButtonClasses(): string
    {
        return 'nxtf-repeater-add-btn';
    }
}
