<?php
declare(strict_types=1);

namespace Celena\Module\CustomFields;

use Celena\Core\Database\Connection;
use Celena\Core\Http\Request;
use Celena\Core\Http\Response;
use Celena\Core\Plugin\Hook;
use Celena\Core\Security\Auth;
use Celena\Core\Security\Csrf;
use Celena\Core\Security\Filter;
use Celena\Core\Security\Permission;
use Celena\Core\Template\Engine;
use Celena\Module\AdminBase\AdminController;
use Celena\Module\Settings\Options;

/**
 * Управление доп. полями: описания кастомных полей, которые можно подвязать
 * к любой сущности (новость, страница, товар и т.п.).
 *
 * Сущности регистрируются ядром + расширяются плагинами через фильтр-хук
 * `customfields.entities`, возвращающий массив items вида
 * [['slug' => 'shop_product', 'label' => 'Товары']].
 *
 * Настройки каждого поля (опции списка, min/max, placeholder и т.д.) хранятся
 * одной JSON-колонкой `options` — это даёт гибкость без миграций БД.
 */
final class CustomFieldsController extends AdminController
{
    /**
     * Типы полей, доступные в админке.
     * Каждый тип: человечное имя, описание, иконка для карточки выбора,
     * набор настроек (ключи JSON-опций, которые рисует UI редактора).
     *
     * @var array<string, array{label:string, hint:string, icon:string, settings:string[]}>
     */
    private const FIELD_TYPES = [
        'text'        => ['label' => 'Короткий текст',    'hint' => 'Одна строка: имя, артикул, фраза.',            'icon' => 'edit',        'settings' => ['placeholder', 'maxlength', 'default']],
        'textarea'    => ['label' => 'Длинный текст',     'hint' => 'Несколько строк: описание, примечание.',       'icon' => 'pages',       'settings' => ['placeholder', 'rows', 'maxlength', 'default']],
        'number'      => ['label' => 'Число',             'hint' => 'Цена, количество, процент.',                   'icon' => 'plus',        'settings' => ['min', 'max', 'step', 'default_number']],
        'checkbox'    => ['label' => 'Галочка (да/нет)',  'hint' => 'Одна опция: включено или нет.',                'icon' => 'check',       'settings' => ['default_bool']],
        'select'      => ['label' => 'Список (один выбор)','hint' => 'Выбрать один вариант из нескольких.',         'icon' => 'chevron_down','settings' => ['values', 'default']],
        'multiselect' => ['label' => 'Список (несколько)','hint' => 'Выбрать несколько вариантов одновременно.',    'icon' => 'categories',  'settings' => ['values']],
        'date'        => ['label' => 'Дата',              'hint' => 'Календарь для выбора даты.',                   'icon' => 'calendar',    'settings' => ['min_date', 'max_date', 'default']],
        'url'         => ['label' => 'Ссылка (URL)',      'hint' => 'Интернет-адрес, начиная с http:// или https://.', 'icon' => 'link',     'settings' => ['placeholder', 'default']],
        'email'       => ['label' => 'Email',             'hint' => 'Адрес электронной почты с проверкой формата.', 'icon' => 'comments',    'settings' => ['placeholder', 'default']],
        'image'       => ['label' => 'Изображение',       'hint' => 'Загрузка картинки или выбор из библиотеки.',   'icon' => 'image',       'settings' => ['image_count', 'image_max_size', 'image_quality']],
    ];

    public function __construct(
        Engine $engine,
        Auth $auth,
        Options $options,
        private readonly Connection $conn,
    ) {
        parent::__construct($engine, $auth, $options);
    }

    public function index(Request $request): Response
    {
        if (!Permission::can($this->auth->user(), 'customfields.read')) {
            return $this->redirect('/admin', 'Доступ запрещён.', 'error');
        }
        $rows = $this->conn->builder('celena_custom_fields')
            ->orderBy('entity_type', 'asc')->orderBy('sort_order', 'asc')
            ->get();

        $entities = $this->entityTypes();
        $entityLabels = [];
        foreach ($entities as $e) {
            $entityLabels[$e['slug']] = $e['label'];
        }
        foreach ($rows as &$r) {
            $r['type_label'] = self::FIELD_TYPES[$r['type']]['label'] ?? $r['type'];
            $r['entity_label'] = $entityLabels[$r['entity_type']] ?? $r['entity_type'];
        }
        unset($r);

        return $this->view('admin/custom-fields/index', [
            'fields' => $rows,
        ], 'custom-fields', 'Дополнительные поля');
    }

    public function create(Request $request): Response
    {
        return $this->view('admin/custom-fields/form', [
            'row' => $this->emptyRow(),
            'types' => $this->typesForTemplate(),
            'entities' => $this->entityTypes(),
            'settings' => $this->prepareSettings([]),
            'form_action' => '/admin/custom-fields/create',
        ], 'custom-fields', 'Новое поле');
    }

    public function store(Request $request): Response
    {
        if (!Csrf::validate((string) $request->input('_csrf'))) {
            return $this->redirect('/admin/custom-fields/create', 'CSRF.', 'error');
        }
        $data = $this->extract($request);
        if ($data['label'] === '') {
            return $this->redirect('/admin/custom-fields/create', 'Название обязательно.', 'error');
        }
        if ($data['slug'] === '') {
            $data['slug'] = Filter::slug($data['label']);
        }
        if ($this->slugExists($data['slug'])) {
            return $this->redirect('/admin/custom-fields/create', 'Ключ (slug) уже занят. Выберите другое название или измените ключ вручную.', 'error');
        }
        $data['created_at'] = date('Y-m-d H:i:s');
        $this->conn->builder('celena_custom_fields')->insert($data);
        return $this->redirect('/admin/custom-fields', 'Поле создано.');
    }

    public function edit(Request $request, array $params): Response
    {
        $id = Filter::int($params['id'] ?? 0);
        $row = $this->conn->builder('celena_custom_fields')->where('id', '=', $id)->first();
        if ($row === null) {
            return $this->redirect('/admin/custom-fields', 'Не найдено.', 'error');
        }
        $settings = json_decode((string) ($row['options'] ?? ''), true);
        if (!is_array($settings)) $settings = [];
        return $this->view('admin/custom-fields/form', [
            'row' => $row,
            'types' => $this->typesForTemplate(),
            'entities' => $this->entityTypes(),
            'settings' => $this->prepareSettings($settings),
            'form_action' => '/admin/custom-fields/' . $id . '/update',
        ], 'custom-fields', 'Редактировать поле');
    }

    public function update(Request $request, array $params): Response
    {
        if (!Csrf::validate((string) $request->input('_csrf'))) {
            return $this->redirect('/admin/custom-fields', 'CSRF.', 'error');
        }
        $id = Filter::int($params['id'] ?? 0);
        $existing = $this->conn->builder('celena_custom_fields')->where('id', '=', $id)->first();
        if ($existing === null) {
            return $this->redirect('/admin/custom-fields', 'Не найдено.', 'error');
        }
        $data = $this->extract($request);
        if ($data['slug'] === '') {
            $data['slug'] = Filter::slug($data['label']);
        }
        if ($data['slug'] !== $existing['slug'] && $this->slugExists($data['slug'])) {
            return $this->redirect('/admin/custom-fields/' . $id . '/edit', 'Ключ (slug) занят.', 'error');
        }
        $this->conn->builder('celena_custom_fields')->where('id', '=', $id)->update($data);
        return $this->redirect('/admin/custom-fields', 'Сохранено.');
    }

    public function delete(Request $request, array $params): Response
    {
        if (!Csrf::validate((string) $request->input('_csrf'))) {
            return $this->redirect('/admin/custom-fields', 'CSRF.', 'error');
        }
        $id = Filter::int($params['id'] ?? 0);
        $this->conn->builder('celena_custom_field_values')->where('field_id', '=', $id)->delete();
        $this->conn->builder('celena_custom_fields')->where('id', '=', $id)->delete();
        return $this->redirect('/admin/custom-fields', 'Поле удалено.');
    }

    /**
     * Собрать настройки из POST-значения каждого подпункта (values[], min, max, ...)
     * в один JSON для колонки `options`.
     */
    private function extract(Request $request): array
    {
        $type = Filter::in($request->input('type'), array_keys(self::FIELD_TYPES), 'text');
        $entities = array_column($this->entityTypes(), 'slug');
        if ($entities === []) $entities = ['news'];

        $opts = [];
        $def = self::FIELD_TYPES[$type]['settings'] ?? [];
        foreach ($def as $key) {
            $raw = $request->input('opt_' . $key);
            switch ($key) {
                case 'values':
                    $vals = $request->input('opt_values');
                    if (is_array($vals)) {
                        $clean = [];
                        foreach ($vals as $v) {
                            $v = trim((string) $v);
                            if ($v !== '') $clean[] = $v;
                        }
                        if ($clean !== []) $opts['values'] = $clean;
                    }
                    break;
                case 'min': case 'max': case 'step': case 'default_number':
                    if ($raw !== null && $raw !== '') $opts[$key === 'default_number' ? 'default' : $key] = (float) $raw;
                    break;
                case 'rows': case 'maxlength':
                    if ($raw !== null && $raw !== '') $opts[$key] = max(1, (int) $raw);
                    break;
                case 'image_count':
                    if ($raw !== null && $raw !== '') $opts['image_count'] = max(1, min(50, (int) $raw));
                    break;
                case 'image_max_size':
                    if ($raw !== null && $raw !== '') $opts['image_max_size'] = max(200, min(8000, (int) $raw));
                    break;
                case 'image_quality':
                    if ($raw !== null && $raw !== '') $opts['image_quality'] = max(10, min(100, (int) $raw));
                    break;
                case 'default_bool':
                    $opts['default'] = Filter::bool($raw);
                    break;
                case 'min_date': case 'max_date':
                    $raw = trim((string) $raw);
                    if ($raw !== '') $opts[$key] = $raw;
                    break;
                case 'placeholder': case 'default':
                    $raw = trim((string) $raw);
                    if ($raw !== '') $opts[$key] = $raw;
                    break;
            }
        }

        $help = trim((string) $request->input('help'));
        if ($help !== '') $opts['help'] = $help;

        return [
            'slug' => Filter::slug($request->input('slug')),
            'label' => Filter::string($request->input('label'), 191),
            'type' => $type,
            'entity_type' => Filter::in($request->input('entity_type'), $entities, $entities[0]),
            'options' => $opts === [] ? '' : json_encode($opts, JSON_UNESCAPED_UNICODE),
            'is_required' => Filter::bool($request->input('is_required')),
            'is_searchable' => Filter::bool($request->input('is_searchable')),
            'sort_order' => Filter::int($request->input('sort_order'), 0),
        ];
    }

    /**
     * Список сущностей, для которых можно создавать поля. Ядро знает про
     * new/page; плагины расширяют через хук `customfields.entities`.
     *
     * @return array<int, array{slug:string, label:string}>
     */
    private function entityTypes(): array
    {
        $core = [
            ['slug' => 'news', 'label' => 'Новости'],
            ['slug' => 'page', 'label' => 'Страницы'],
        ];
        $all = Hook::filter('customfields.entities', $core);
        if (!is_array($all) || $all === []) return $core;
        $seen = [];
        $out = [];
        foreach ($all as $e) {
            if (!is_array($e) || !isset($e['slug'])) continue;
            $slug = (string) $e['slug'];
            if (isset($seen[$slug])) continue;
            $seen[$slug] = true;
            $out[] = [
                'slug'  => $slug,
                'label' => (string) ($e['label'] ?? $slug),
            ];
        }
        return $out;
    }

    /** @return array<int, array{slug:string, label:string, hint:string, svg:string, settings:string[]}> */
    private function typesForTemplate(): array
    {
        $iconTag = new \Celena\Core\Template\Tags\IconTag();
        $out = [];
        foreach (self::FIELD_TYPES as $slug => $def) {
            $out[] = [
                'slug' => $slug,
                'label' => $def['label'],
                'hint' => $def['hint'],
                'svg' => $iconTag(['name' => $def['icon'], 'size' => 22]),
                'settings' => $def['settings'],
            ];
        }
        return $out;
    }

    /**
     * Готовим settings для шаблона: все скалярные поля остаются как есть,
     * массив values дополнительно сериализуем в JSON — шаблон прокидывает его
     * как window.__CF_VALUES__ для инициализации UI редактора опций.
     * @param array<string,mixed> $settings
     * @return array<string,mixed>
     */
    private function prepareSettings(array $settings): array
    {
        $out = [
            'placeholder' => (string) ($settings['placeholder'] ?? ''),
            'maxlength'   => isset($settings['maxlength']) ? (string) $settings['maxlength'] : '',
            'rows'        => isset($settings['rows']) ? (string) $settings['rows'] : '',
            'min'         => isset($settings['min']) ? (string) $settings['min'] : '',
            'max'         => isset($settings['max']) ? (string) $settings['max'] : '',
            'step'        => isset($settings['step']) ? (string) $settings['step'] : '',
            'default'     => (string) ($settings['default'] ?? ''),
            'min_date'    => (string) ($settings['min_date'] ?? ''),
            'max_date'    => (string) ($settings['max_date'] ?? ''),
            'help'        => (string) ($settings['help'] ?? ''),
            'image_count'    => isset($settings['image_count']) ? (string) $settings['image_count'] : '1',
            'image_max_size' => isset($settings['image_max_size']) ? (string) $settings['image_max_size'] : '',
            'image_quality'  => isset($settings['image_quality']) ? (string) $settings['image_quality'] : '',
            'values_json' => json_encode(array_values((array) ($settings['values'] ?? [])), JSON_UNESCAPED_UNICODE),
        ];
        return $out;
    }

    private function emptyRow(): array
    {
        return [
            'id' => 0, 'slug' => '', 'label' => '', 'type' => 'text',
            'entity_type' => ($this->entityTypes()[0]['slug'] ?? 'news'),
            'options' => '',
            'is_required' => false, 'is_searchable' => false, 'sort_order' => 0,
        ];
    }

    private function slugExists(string $slug): bool
    {
        return $this->conn->builder('celena_custom_fields')->where('slug', '=', $slug)->first() !== null;
    }
}
