<?php

namespace App\Services;

/**
 * 블록 스키마를 HTML로 렌더링하는 서비스
 *
 * 3가지 모드 지원:
 * - view: 읽기 전용 (문서 상세 조회)
 * - edit: 입력 폼 (문서 생성/수정)
 * - print: 인쇄/미리보기 전용
 */
class BlockRendererService
{
    protected string $mode = 'view'; // view | edit | print

    /**
     * JSON 블록 스키마를 HTML로 렌더링
     *
     * @param  array  $schema  블록 스키마 (blocks, page)
     * @param  string  $mode  렌더링 모드 (view|edit|print)
     * @param  array  $data  바인딩 데이터 (field_key => value)
     */
    public function render(array $schema, string $mode = 'view', array $data = []): string
    {
        $this->mode = $mode;
        $blocks = $schema['blocks'] ?? [];
        $page = $schema['page'] ?? [];

        $html = $this->renderPageOpen($page);

        foreach ($blocks as $block) {
            $html .= $this->renderBlock($block, $data);
        }

        $html .= $this->renderPageClose();

        return $html;
    }

    /**
     * 개별 블록 렌더링
     */
    public function renderBlock(array $block, array $data = []): string
    {
        $type = $block['type'] ?? '';
        $props = $block['props'] ?? [];
        $blockId = $block['id'] ?? '';

        return match ($type) {
            'heading' => $this->renderHeading($props, $data),
            'paragraph' => $this->renderParagraph($props, $data),
            'table' => $this->renderTable($props, $blockId, $data),
            'columns' => $this->renderColumns($props, $data),
            'divider' => $this->renderDivider($props),
            'spacer' => $this->renderSpacer($props),
            'text_field' => $this->renderTextField($props, $blockId, $data),
            'number_field' => $this->renderNumberField($props, $blockId, $data),
            'date_field' => $this->renderDateField($props, $blockId, $data),
            'select_field' => $this->renderSelectField($props, $blockId, $data),
            'checkbox_field' => $this->renderCheckboxField($props, $blockId, $data),
            'textarea_field' => $this->renderTextareaField($props, $blockId, $data),
            'signature_field' => $this->renderSignatureField($props, $blockId, $data),
            default => "<!-- Unknown block type: {$type} -->",
        };
    }

    /**
     * 블록 스키마에서 폼 필드의 binding 목록 추출
     * edit.blade.php에서 JavaScript 데이터 수집 시 사용
     */
    public function extractBindings(array $schema): array
    {
        $bindings = [];
        $formTypes = ['text_field', 'number_field', 'date_field', 'select_field', 'checkbox_field', 'textarea_field', 'signature_field'];

        foreach ($schema['blocks'] ?? [] as $block) {
            $type = $block['type'] ?? '';
            if (in_array($type, $formTypes)) {
                $binding = $block['props']['binding'] ?? '';
                $blockId = $block['id'] ?? '';
                $fieldKey = $binding ?: 'block_'.$blockId;
                $bindings[] = [
                    'block_id' => $blockId,
                    'type' => $type,
                    'field_key' => $fieldKey,
                    'label' => $block['props']['label'] ?? '',
                    'required' => ! empty($block['props']['required']),
                ];
            }
            // columns 내부 블록도 탐색
            if ($type === 'columns') {
                foreach ($block['props']['children'] ?? [] as $children) {
                    foreach ($children as $child) {
                        $childType = $child['type'] ?? '';
                        if (in_array($childType, $formTypes)) {
                            $binding = $child['props']['binding'] ?? '';
                            $childId = $child['id'] ?? '';
                            $fieldKey = $binding ?: 'block_'.$childId;
                            $bindings[] = [
                                'block_id' => $childId,
                                'type' => $childType,
                                'field_key' => $fieldKey,
                                'label' => $child['props']['label'] ?? '',
                                'required' => ! empty($child['props']['required']),
                            ];
                        }
                    }
                }
            }
        }

        return $bindings;
    }

    /**
     * 데이터 바인딩 치환 (변수 해석)
     */
    protected function resolveBinding(string $text, array $data): string
    {
        return preg_replace_callback('/\{\{(.+?)\}\}/', function ($matches) use ($data) {
            $key = trim($matches[1]);

            if ($key === 'today') {
                return now()->format('Y-m-d');
            }
            if ($key === 'now') {
                return now()->format('Y-m-d H:i');
            }

            return data_get($data, $key, $matches[0]);
        }, $text);
    }

    /**
     * 폼 필드의 field_key 결정: binding이 있으면 binding, 없으면 block_{id}
     */
    protected function getFieldKey(array $props, string $blockId): string
    {
        $binding = $props['binding'] ?? '';

        return $binding !== '' ? $binding : 'block_'.$blockId;
    }

    /**
     * EAV 데이터에서 값 조회
     */
    protected function getFieldValue(string $fieldKey, array $data, string $default = ''): string
    {
        return (string) ($data[$fieldKey] ?? $default);
    }

    protected function isEditMode(): bool
    {
        return $this->mode === 'edit';
    }

    protected function isViewMode(): bool
    {
        return $this->mode === 'view';
    }

    protected function isPrintMode(): bool
    {
        return $this->mode === 'print';
    }

    // ===== 레이아웃 블록 렌더링 =====

    protected function renderPageOpen(array $page): string
    {
        $size = $page['size'] ?? 'A4';
        $orientation = $page['orientation'] ?? 'portrait';
        $printStyle = $this->isPrintMode() ? 'max-width: 210mm; margin: 0 auto; padding: 20mm 15mm;' : '';

        return '<div class="block-document" data-page-size="'.e($size).'" data-orientation="'.e($orientation).'" style="font-family: \'Noto Sans KR\', sans-serif; '.$printStyle.'">';
    }

    protected function renderPageClose(): string
    {
        return '</div>';
    }

    protected function renderHeading(array $props, array $data): string
    {
        $level = min(max(intval($props['level'] ?? 2), 1), 6);
        $text = e($this->resolveBinding($props['text'] ?? '', $data));
        $align = e($props['align'] ?? 'left');

        $sizes = [1 => '1.5em', 2 => '1.25em', 3 => '1.1em', 4 => '1em', 5 => '0.9em', 6 => '0.85em'];
        $size = $sizes[$level];

        return "<h{$level} style=\"text-align:{$align}; font-size:{$size}; font-weight:bold; margin:0.5em 0;\">{$text}</h{$level}>";
    }

    protected function renderParagraph(array $props, array $data): string
    {
        $text = e($this->resolveBinding($props['text'] ?? '', $data));
        $text = nl2br($text);
        $align = e($props['align'] ?? 'left');

        return "<p style=\"text-align:{$align}; margin:0.3em 0; font-size:0.9em; line-height:1.6;\">{$text}</p>";
    }

    protected function renderTable(array $props, string $blockId, array $data): string
    {
        $headers = $props['headers'] ?? [];
        $rows = $props['rows'] ?? [];
        $showHeader = $props['showHeader'] ?? true;

        $html = '<table style="width:100%; border-collapse:collapse; border:1px solid #333; margin:0.5em 0; font-size:0.85em;">';

        if ($showHeader && ! empty($headers)) {
            $html .= '<thead><tr>';
            foreach ($headers as $header) {
                $html .= '<th style="border:1px solid #333; padding:4px 8px; background:#f5f5f5; font-weight:bold; text-align:center;">'.e($header).'</th>';
            }
            $html .= '</tr></thead>';
        }

        $html .= '<tbody>';
        foreach ($rows as $rIdx => $row) {
            $html .= '<tr>';
            foreach ($row as $cIdx => $cell) {
                $cellKey = "tbl_{$blockId}_r{$rIdx}_c{$cIdx}";
                $savedValue = $this->getFieldValue($cellKey, $data, '');
                $cellText = $cell ?? '';

                if ($this->isEditMode()) {
                    $displayValue = $savedValue !== '' ? $savedValue : $cellText;
                    $html .= '<td style="border:1px solid #333; padding:2px 4px;">';
                    $html .= '<input type="text" value="'.e($displayValue).'" data-field-key="'.e($cellKey).'" class="w-full border-0 bg-transparent text-sm p-0 focus:ring-0" style="outline:none;">';
                    $html .= '</td>';
                } else {
                    $displayValue = $savedValue !== '' ? $savedValue : $cellText;
                    $value = $this->resolveBinding($displayValue, $data);
                    $html .= '<td style="border:1px solid #333; padding:4px 8px;">'.e($value).'</td>';
                }
            }
            $html .= '</tr>';
        }
        $html .= '</tbody></table>';

        return $html;
    }

    protected function renderColumns(array $props, array $data): string
    {
        $count = intval($props['count'] ?? 2);
        $children = $props['children'] ?? [];

        $html = '<div style="display:flex; gap:10px; margin:0.5em 0;">';

        for ($i = 0; $i < $count; $i++) {
            $html .= '<div style="flex:1;">';
            $colChildren = $children[$i] ?? [];
            foreach ($colChildren as $child) {
                $html .= $this->renderBlock($child, $data);
            }
            $html .= '</div>';
        }

        $html .= '</div>';

        return $html;
    }

    protected function renderDivider(array $props): string
    {
        $style = ($props['style'] ?? 'solid') === 'dashed' ? 'dashed' : 'solid';

        return "<hr style=\"border:none; border-top:1px {$style} #ccc; margin:0.8em 0;\">";
    }

    protected function renderSpacer(array $props): string
    {
        $height = max(intval($props['height'] ?? 20), 0);

        return "<div style=\"height:{$height}px;\"></div>";
    }

    // ===== 폼 필드 블록 렌더링 =====

    protected function renderTextField(array $props, string $blockId, array $data): string
    {
        $label = e($props['label'] ?? '');
        $fieldKey = $this->getFieldKey($props, $blockId);
        $value = $this->getFieldValue($fieldKey, $data);
        $required = ! empty($props['required']);
        $requiredMark = $required ? ' <span style="color:red;">*</span>' : '';
        $placeholder = e($props['placeholder'] ?? '');

        if ($this->isEditMode()) {
            $requiredAttr = $required ? 'required' : '';

            return '<div style="margin:0.4em 0;">'
                .'<label class="block text-sm font-medium text-gray-700 mb-1">'.$label.$requiredMark.'</label>'
                .'<input type="text" value="'.e($value).'" placeholder="'.($placeholder ?: $label).'" data-field-key="'.e($fieldKey).'" '.$requiredAttr
                .' class="w-full rounded-lg border-gray-300 text-sm focus:border-blue-500 focus:ring-blue-500">'
                .'</div>';
        }

        // view / print
        $displayValue = $value !== '' ? e($value) : '<span style="color:#999;">-</span>';

        return '<div style="margin:0.3em 0;"><label style="font-size:0.8em; font-weight:bold; display:block; margin-bottom:2px;">'.$label.$requiredMark.'</label><div style="border-bottom:1px solid #ddd; padding:4px 0; min-height:24px; font-size:0.9em;">'.$displayValue.'</div></div>';
    }

    protected function renderNumberField(array $props, string $blockId, array $data): string
    {
        $label = e($props['label'] ?? '');
        $fieldKey = $this->getFieldKey($props, $blockId);
        $value = $this->getFieldValue($fieldKey, $data);
        $unit = e($props['unit'] ?? '');
        $unitDisplay = $unit ? " ({$unit})" : '';
        $min = $props['min'] ?? null;
        $max = $props['max'] ?? null;
        $decimal = intval($props['decimal'] ?? 0);

        if ($this->isEditMode()) {
            $attrs = '';
            if ($min !== null) {
                $attrs .= ' min="'.e($min).'"';
            }
            if ($max !== null) {
                $attrs .= ' max="'.e($max).'"';
            }
            if ($decimal > 0) {
                $attrs .= ' step="'.e(1 / pow(10, $decimal)).'"';
            }

            return '<div style="margin:0.4em 0;">'
                .'<label class="block text-sm font-medium text-gray-700 mb-1">'.$label.$unitDisplay.'</label>'
                .'<input type="number" value="'.e($value).'" data-field-key="'.e($fieldKey).'"'.$attrs
                .' class="w-full rounded-lg border-gray-300 text-sm focus:border-blue-500 focus:ring-blue-500">'
                .'</div>';
        }

        $displayValue = $value !== '' ? e($value).$unitDisplay : '<span style="color:#999;">-</span>';

        return '<div style="margin:0.3em 0;"><label style="font-size:0.8em; font-weight:bold; display:block; margin-bottom:2px;">'.$label.$unitDisplay.'</label><div style="border-bottom:1px solid #ddd; padding:4px 0; min-height:24px; font-size:0.9em;">'.$displayValue.'</div></div>';
    }

    protected function renderDateField(array $props, string $blockId, array $data): string
    {
        $label = e($props['label'] ?? '');
        $fieldKey = $this->getFieldKey($props, $blockId);
        $value = $this->getFieldValue($fieldKey, $data);

        if ($this->isEditMode()) {
            $defaultValue = $value !== '' ? $value : ($this->resolveBinding($props['default'] ?? '', $data) ?: '');

            return '<div style="margin:0.4em 0;">'
                .'<label class="block text-sm font-medium text-gray-700 mb-1">'.$label.'</label>'
                .'<input type="date" value="'.e($defaultValue).'" data-field-key="'.e($fieldKey).'"'
                .' class="w-full rounded-lg border-gray-300 text-sm focus:border-blue-500 focus:ring-blue-500">'
                .'</div>';
        }

        $displayValue = $value !== '' ? e($value) : '<span style="color:#999;">-</span>';

        return '<div style="margin:0.3em 0;"><label style="font-size:0.8em; font-weight:bold; display:block; margin-bottom:2px;">'.$label.'</label><div style="border-bottom:1px solid #ddd; padding:4px 0; min-height:24px; font-size:0.9em;">'.$displayValue.'</div></div>';
    }

    protected function renderSelectField(array $props, string $blockId, array $data): string
    {
        $label = e($props['label'] ?? '');
        $fieldKey = $this->getFieldKey($props, $blockId);
        $value = $this->getFieldValue($fieldKey, $data);
        $options = $props['options'] ?? [];

        if ($this->isEditMode()) {
            $html = '<div style="margin:0.4em 0;">'
                .'<label class="block text-sm font-medium text-gray-700 mb-1">'.$label.'</label>'
                .'<select data-field-key="'.e($fieldKey).'" class="w-full rounded-lg border-gray-300 text-sm focus:border-blue-500 focus:ring-blue-500">'
                .'<option value="">선택하세요</option>';
            foreach ($options as $opt) {
                $selected = $value === $opt ? ' selected' : '';
                $html .= '<option value="'.e($opt).'"'.$selected.'>'.e($opt).'</option>';
            }
            $html .= '</select></div>';

            return $html;
        }

        $displayValue = $value !== '' ? e($value) : '<span style="color:#999;">-</span>';

        return '<div style="margin:0.3em 0;"><label style="font-size:0.8em; font-weight:bold; display:block; margin-bottom:2px;">'.$label.'</label><div style="border-bottom:1px solid #ddd; padding:4px 0; min-height:24px; font-size:0.9em;">'.$displayValue.'</div></div>';
    }

    protected function renderCheckboxField(array $props, string $blockId, array $data): string
    {
        $label = e($props['label'] ?? '');
        $fieldKey = $this->getFieldKey($props, $blockId);
        $savedValues = array_filter(explode(',', $this->getFieldValue($fieldKey, $data)));
        $options = $props['options'] ?? [];

        if ($this->isEditMode()) {
            $html = '<div style="margin:0.4em 0;">'
                .'<label class="block text-sm font-medium text-gray-700 mb-1">'.$label.'</label>'
                .'<div class="flex flex-wrap gap-3 mt-1">';
            foreach ($options as $idx => $opt) {
                $checked = in_array($opt, $savedValues) ? ' checked' : '';
                $html .= '<label class="flex items-center gap-1.5 text-sm text-gray-700">'
                    .'<input type="checkbox" value="'.e($opt).'" data-field-key="'.e($fieldKey).'" data-checkbox-group="'.e($fieldKey).'"'.$checked
                    .' class="rounded border-gray-300 text-blue-600 focus:ring-blue-500">'
                    .e($opt).'</label>';
            }
            $html .= '</div></div>';

            return $html;
        }

        // view / print
        $html = '<div style="margin:0.3em 0;"><label style="font-size:0.8em; font-weight:bold; display:block; margin-bottom:2px;">'.$label.'</label><div style="display:flex; gap:12px; flex-wrap:wrap;">';
        foreach ($options as $opt) {
            $checked = in_array($opt, $savedValues) ? '☑' : '☐';
            $html .= '<span style="font-size:0.85em;">'.$checked.' '.e($opt).'</span>';
        }
        $html .= '</div></div>';

        return $html;
    }

    protected function renderTextareaField(array $props, string $blockId, array $data): string
    {
        $label = e($props['label'] ?? '');
        $fieldKey = $this->getFieldKey($props, $blockId);
        $value = $this->getFieldValue($fieldKey, $data);
        $rows = max(intval($props['rows'] ?? 3), 1);

        if ($this->isEditMode()) {
            return '<div style="margin:0.4em 0;">'
                .'<label class="block text-sm font-medium text-gray-700 mb-1">'.$label.'</label>'
                .'<textarea data-field-key="'.e($fieldKey).'" rows="'.$rows.'"'
                .' class="w-full rounded-lg border-gray-300 text-sm focus:border-blue-500 focus:ring-blue-500">'.e($value).'</textarea>'
                .'</div>';
        }

        $displayValue = $value !== '' ? nl2br(e($value)) : '<span style="color:#999;">-</span>';
        $height = $rows * 24;

        return '<div style="margin:0.3em 0;"><label style="font-size:0.8em; font-weight:bold; display:block; margin-bottom:2px;">'.$label.'</label><div style="border:1px solid #eee; padding:4px 8px; min-height:'.$height.'px; font-size:0.9em; white-space:pre-wrap;">'.$displayValue.'</div></div>';
    }

    protected function renderSignatureField(array $props, string $blockId, array $data): string
    {
        $label = e($props['label'] ?? '서명');
        $fieldKey = $this->getFieldKey($props, $blockId);
        $value = $this->getFieldValue($fieldKey, $data);

        if ($this->isEditMode()) {
            return '<div style="margin:0.5em 0;">'
                .'<label class="block text-sm font-medium text-gray-700 mb-1">'.$label.'</label>'
                .'<div style="border:2px dashed #d1d5db; height:80px; display:flex; align-items:center; justify-content:center; border-radius:8px; cursor:pointer;" data-field-key="'.e($fieldKey).'" data-block-type="signature">'
                .'<span class="text-sm text-gray-400">클릭하여 서명</span>'
                .'</div></div>';
        }

        if ($value) {
            return '<div style="margin:0.5em 0;"><label style="font-size:0.8em; font-weight:bold; display:block; margin-bottom:2px;">'.$label.'</label><img src="'.e($value).'" style="max-height:60px;" alt="서명"></div>';
        }

        return '<div style="margin:0.5em 0;"><label style="font-size:0.8em; font-weight:bold; display:block; margin-bottom:2px;">'.$label.'</label><div style="border:2px dashed #ccc; height:60px; display:flex; align-items:center; justify-content:center; font-size:0.8em; color:#999;">서명 영역</div></div>';
    }
}
