<?php
declare(strict_types=1);

namespace Celena\Module\Menus;

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

/**
 * Меню сайта — именованные коллекции пунктов навигации (header, footer и т.д.).
 * Каждое меню → набор пунктов с parent_id (двух уровней достаточно
 * для большинства тем). Темы выводят их через тег {menu name="header"} (план).
 */
final class MenusController extends AdminController
{
    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 ($deny = $this->deny('menus.manage')) return $deny;
        $rows = $this->conn->builder('celena_menus')->orderBy('id', 'asc')->get();
        foreach ($rows as &$r) {
            $r['items_count'] = $this->conn->builder('celena_menu_items')
                ->where('menu_id', '=', (int) $r['id'])->count();
        }
        unset($r);
        return $this->view('admin/menus/index', [
            'menus' => $rows,
            'total' => count($rows),
        ], 'menus', 'Меню сайта');
    }

    public function create(Request $request): Response
    {
        if ($deny = $this->deny('menus.manage')) return $deny;
        return $this->view('admin/menus/form', [
            'row' => ['id' => 0, 'slug' => '', 'title' => ''],
            'form_action' => '/admin/menus/create',
        ], 'menus', 'Новое меню');
    }

    public function store(Request $request): Response
    {
        if ($deny = $this->deny('menus.manage')) return $deny;
        if (!Csrf::validate((string) $request->input('_csrf'))) {
            return $this->redirect('/admin/menus/create', 'CSRF.', 'error');
        }
        $title = Filter::string($request->input('title'), 200);
        $slug  = Filter::urlSlug($request->input('slug', ''));
        if ($title === '') {
            return $this->redirect('/admin/menus/create', 'Название обязательно.', 'error');
        }
        if ($slug === '') $slug = Filter::slug($title);
        if ($this->conn->builder('celena_menus')->where('slug', '=', $slug)->first() !== null) {
            return $this->redirect('/admin/menus/create', 'Slug занят.', 'error');
        }
        $now = date('Y-m-d H:i:s');
        $this->conn->builder('celena_menus')->insert([
            'slug' => $slug, 'title' => $title,
            'created_at' => $now, 'updated_at' => $now,
        ]);
        return $this->redirect('/admin/menus', 'Меню создано.');
    }

    public function edit(Request $request, array $params): Response
    {
        if ($deny = $this->deny('menus.manage')) return $deny;
        $id = Filter::int($params['id'] ?? 0);
        $menu = $this->conn->builder('celena_menus')->where('id', '=', $id)->first();
        if ($menu === null) return $this->redirect('/admin/menus', 'Не найдено.', 'error');
        $items = $this->conn->builder('celena_menu_items')
            ->where('menu_id', '=', $id)
            ->orderBy('sort_order', 'asc')->orderBy('id', 'asc')
            ->get();
        // Список потенциальных «родителей» — пункты этого же меню с parent_id null.
        $parents = array_values(array_filter($items, fn ($i) => empty($i['parent_id'])));

        return $this->view('admin/menus/edit', [
            'menu'        => $menu,
            'items'       => $items,
            'items_count' => count($items),
            'parents'     => $parents,
        ], 'menus', 'Меню: ' . $menu['title']);
    }

    public function delete(Request $request, array $params): Response
    {
        if ($deny = $this->deny('menus.manage')) return $deny;
        if (!Csrf::validate((string) $request->input('_csrf'))) {
            return $this->redirect('/admin/menus', 'CSRF.', 'error');
        }
        $id = Filter::int($params['id'] ?? 0);
        $this->conn->builder('celena_menu_items')->where('menu_id', '=', $id)->delete();
        $this->conn->builder('celena_menus')->where('id', '=', $id)->delete();
        return $this->redirect('/admin/menus', 'Удалено.');
    }

    public function itemStore(Request $request, array $params): Response
    {
        if ($deny = $this->deny('menus.manage')) return $deny;
        if (!Csrf::validate((string) $request->input('_csrf'))) {
            return $this->redirect('/admin/menus', 'CSRF.', 'error');
        }
        $menuId = Filter::int($params['id'] ?? 0);
        $menu = $this->conn->builder('celena_menus')->where('id', '=', $menuId)->first();
        if ($menu === null) return $this->redirect('/admin/menus', 'Не найдено.', 'error');

        $title = Filter::string($request->input('title'), 255);
        if ($title === '') return $this->redirect('/admin/menus/' . $menuId . '/edit', 'Название обязательно.', 'error');

        $kind = (string) $request->input('kind', 'external');
        if (!in_array($kind, ['page', 'news', 'category', 'external', 'divider'], true)) $kind = 'external';

        // URL пункта выводится в href на всех страницах — режем javascript:/data:/vbscript:,
        // иначе пункт меню превращается в clickable XSS (htmlspecialchars это не ловит).
        $url = Filter::string($request->input('url'), 500);
        if (!$this->isSafeMenuUrl($url)) {
            return $this->redirect('/admin/menus/' . $menuId . '/edit', 'Недопустимый URL пункта.', 'error');
        }

        $parentRaw = $request->input('parent_id');
        $parent = ($parentRaw === '' || $parentRaw === null) ? null : Filter::int($parentRaw);

        $this->conn->builder('celena_menu_items')->insert([
            'menu_id'   => $menuId,
            'parent_id' => $parent === 0 ? null : $parent,
            'title'     => $title,
            'url'       => $url,
            'kind'      => $kind,
            'object_id' => Filter::int($request->input('object_id', 0)) ?: null,
            'target'    => in_array($request->input('target'), ['_blank', '_self'], true) ? (string) $request->input('target') : '_self',
            'icon'      => Filter::string($request->input('icon'), 50),
            'sort_order'=> Filter::int($request->input('sort_order'), 0),
            'is_active' => Filter::bool($request->input('is_active')),
            'created_at'=> date('Y-m-d H:i:s'),
        ]);
        return $this->redirect('/admin/menus/' . $menuId . '/edit', 'Пункт добавлен.');
    }

    public function itemDelete(Request $request, array $params): Response
    {
        if ($deny = $this->deny('menus.manage')) return $deny;
        if (!Csrf::validate((string) $request->input('_csrf'))) {
            return $this->redirect('/admin/menus', 'CSRF.', 'error');
        }
        $itemId = Filter::int($params['itemId'] ?? 0);
        $item = $this->conn->builder('celena_menu_items')->where('id', '=', $itemId)->first();
        if ($item === null) return $this->redirect('/admin/menus', 'Не найдено.', 'error');
        $this->conn->builder('celena_menu_items')->where('id', '=', $itemId)->delete();
        // Дочерние пункты тоже отвязываем от родителя.
        $this->conn->builder('celena_menu_items')->where('parent_id', '=', $itemId)->update(['parent_id' => null]);
        return $this->redirect('/admin/menus/' . (int) $item['menu_id'] . '/edit', 'Пункт удалён.');
    }

    /** Допустимые URL пункта меню: относительные, http(s), mailto, tel. Без javascript:/data:/vbscript:. */
    private function isSafeMenuUrl(string $url): bool
    {
        $url = trim($url);
        if ($url === '') return true; // пустой URL допустим (например, для kind=divider)
        if ($url[0] === '/' || $url[0] === '#' || $url[0] === '.' || str_starts_with($url, '?')) return true;
        $scheme = strtolower((string) parse_url($url, PHP_URL_SCHEME));
        if ($scheme === '') return true; // относительный путь без схемы
        return in_array($scheme, ['http', 'https', 'mailto', 'tel'], true);
    }
}
