<?php
declare(strict_types=1);

namespace Celena\Module\Pages;

use Celena\Core\Database\Connection;
use Celena\Core\Http\Pagination;
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\Security\HtmlSanitizer;
use Celena\Core\Security\Permission;
use Celena\Core\Support\Markdown;
use Celena\Core\Template\Engine;
use Celena\Module\AdminBase\AdminController;
use Celena\Module\CustomFields\FieldRenderer;
use Celena\Module\CustomFields\FieldRepository;
use Celena\Module\Settings\Options;

final class PagesController 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 (!Permission::can($this->auth->user(), 'pages.read')) {
            return $this->redirect('/admin', 'Доступ запрещён.', 'error');
        }
        $page = Filter::int($request->query('page', 1), 1);
        $total = $this->conn->builder('celena_pages')->count();
        $p = new Pagination($page, 20, $total);
        $rows = $this->conn->builder('celena_pages')
            ->orderBy('id', 'desc')
            ->limit($p->perPage)->offset($p->offset)
            ->get();
        return $this->view('admin/pages/index', [
            'pages' => $rows,
            'pagination' => $p->toArray('/admin/pages?page=:page'),
        ], 'pages', 'Страницы');
    }

    public function create(Request $request): Response
    {
        if (!Permission::can($this->auth->user(), 'pages.create')) {
            return $this->redirect('/admin', 'Доступ запрещён.', 'error');
        }
        $repo = new FieldRepository($this->conn);
        $fields = FieldRenderer::prepare($repo->fieldsFor('page'), []);
        return $this->view('admin/pages/form', [
            'row' => $this->emptyRow(),
            'custom_fields' => $fields,
            'form_action' => '/admin/pages/create',
        ], 'pages', 'Новая страница');
    }

    public function store(Request $request): Response
    {
        if (!Permission::can($this->auth->user(), 'pages.create')) {
            return $this->redirect('/admin', 'Доступ запрещён.', 'error');
        }
        if (!Csrf::validate((string) $request->input('_csrf'))) {
            return $this->redirect('/admin/pages/create', 'CSRF.', 'error');
        }
        $data = $this->extract($request);
        if ($data['title'] === '') {
            return $this->redirect('/admin/pages/create', 'Заголовок обязателен.', 'error');
        }
        if ($data['slug'] === '') {
            $data['slug'] = Filter::slug($data['title']);
        }
        if ($this->slugExists($data['slug'])) {
            return $this->redirect('/admin/pages/create', 'Slug уже существует.', 'error');
        }
        $now = date('Y-m-d H:i:s');
        $data['author_id'] = $this->auth->id();
        $data['created_at'] = $now;
        $data['updated_at'] = $now;
        if ($data['status'] === 'published' && $data['published_at'] === null) {
            $data['published_at'] = $now;
        }
        $id = $this->conn->builder('celena_pages')->insert($data);
        $cfValues = $request->input('cf');
        if (is_array($cfValues)) {
            (new FieldRepository($this->conn))->saveValues('page', (int) $id, $cfValues);
        }
        return $this->redirect('/admin/pages', 'Страница создана.');
    }

    public function edit(Request $request, array $params): Response
    {
        $id = Filter::int($params['id'] ?? 0);
        $row = $this->conn->builder('celena_pages')->where('id', '=', $id)->first();
        if ($row === null) {
            return $this->redirect('/admin/pages', 'Не найдена.', 'error');
        }
        if (!$this->canManagePage($row, 'update')) {
            return $this->redirect('/admin/pages', 'Доступ запрещён.', 'error');
        }
        $repo = new FieldRepository($this->conn);
        $fields = FieldRenderer::prepare($repo->fieldsFor('page'), $repo->valuesFor('page', $id));
        return $this->view('admin/pages/form', [
            'row' => $row,
            'custom_fields' => $fields,
            'form_action' => '/admin/pages/' . $id . '/update',
        ], 'pages', 'Редактировать: ' . $row['title']);
    }

    public function update(Request $request, array $params): Response
    {
        if (!Csrf::validate((string) $request->input('_csrf'))) {
            return $this->redirect('/admin/pages', 'CSRF.', 'error');
        }
        $id = Filter::int($params['id'] ?? 0);
        $existing = $this->conn->builder('celena_pages')->where('id', '=', $id)->first();
        if ($existing === null) {
            return $this->redirect('/admin/pages', 'Не найдена.', 'error');
        }
        if (!$this->canManagePage($existing, 'update')) {
            return $this->redirect('/admin/pages', 'Доступ запрещён.', 'error');
        }
        $data = $this->extract($request);
        $user = $this->auth->user();
        if (!Permission::can($user, 'pages.update.any')) {
            unset($data['author_id']);
        }
        if ($data['slug'] === '') {
            $data['slug'] = Filter::slug($data['title']);
        }
        if ($data['slug'] !== $existing['slug'] && $this->slugExists($data['slug'])) {
            return $this->redirect('/admin/pages/' . $id . '/edit', 'Slug уже занят.', 'error');
        }
        $data['updated_at'] = date('Y-m-d H:i:s');
        if ($data['status'] === 'published' && $existing['published_at'] === null && $data['published_at'] === null) {
            $data['published_at'] = $data['updated_at'];
        }
        $this->conn->builder('celena_pages')->where('id', '=', $id)->update($data);
        $cfValues = $request->input('cf');
        if (is_array($cfValues)) {
            (new FieldRepository($this->conn))->saveValues('page', $id, $cfValues);
        }
        return $this->redirect('/admin/pages', 'Сохранено.');
    }

    public function delete(Request $request, array $params): Response
    {
        if (!Csrf::validate((string) $request->input('_csrf'))) {
            return $this->redirect('/admin/pages', 'CSRF.', 'error');
        }
        $id = Filter::int($params['id'] ?? 0);
        $existing = $this->conn->builder('celena_pages')->where('id', '=', $id)->first();
        if ($existing === null) {
            return $this->redirect('/admin/pages', 'Не найдена.', 'error');
        }
        if (!$this->canManagePage($existing, 'delete')) {
            return $this->redirect('/admin/pages', 'Доступ запрещён.', 'error');
        }
        $this->conn->builder('celena_pages')->where('id', '=', $id)->delete();
        return $this->redirect('/admin/pages', 'Страница удалена.');
    }

    /**
     * Может ли текущий пользователь редактировать/удалять данную страницу.
     * Логика как в News: pages.{action}.any — любые, pages.{action} — только свои.
     */
    private function canManagePage(array $row, string $action): bool
    {
        $user = $this->auth->user();
        if ($user === null) {
            return false;
        }
        if (Permission::can($user, 'pages.' . $action . '.any')) {
            return true;
        }
        if (!Permission::can($user, 'pages.' . $action)) {
            return false;
        }
        return (int) ($row['author_id'] ?? 0) === (int) ($user['id'] ?? 0);
    }

    /** Фронт: /p/{slug} */
    public function show(Request $request, array $params): Response
    {
        $slug = Filter::urlSlug($params['slug'] ?? '');
        $row = $this->conn->builder('celena_pages')->where('slug', '=', $slug)->first();
        if ($row === null || $row['status'] !== 'published') {
            return Response::notFound();
        }
        $bodyRaw = (string) ($row['body'] ?? '');
        $html = ((string) ($row['body_format'] ?? 'html')) === 'markdown'
            ? Markdown::render($bodyRaw)
            : $bodyRaw;
        $this->engine->setTheme((string) $this->options->get('theme', 'default'));
        $view = $this->engine->render('page', [
            'site' => [
                'name' => (string) $this->options->get('site_name', ''),
                'url' => (string) $this->options->get('site_url', ''),
            ],
            // Заголовок/описание страницы в <title>/<meta> (head.tpl читает *_override).
            'seo_title_override' => (string) (($row['seo_title'] ?? '') !== '' ? $row['seo_title'] : $row['title']),
            'seo_desc_override' => (string) ($row['seo_description'] ?? ''),
            'page' => [
                'title' => (string) $row['title'],
                'seo_title' => (string) ($row['seo_title'] ?? $row['title']),
                'seo_description' => (string) ($row['seo_description'] ?? ''),
                'og_image' => (string) ($row['og_image'] ?? ''),
            ],
            'content' => $html,
            'raw_body' => (string) ($row['body'] ?? ''),
        ]);
        return Response::html($view);
    }

    private function emptyRow(): array
    {
        return [
            'id' => 0, 'slug' => '', 'title' => '', 'body' => '', 'body_format' => 'html',
            'status' => 'draft', 'seo_title' => '', 'seo_description' => '', 'og_image' => '',
            'published_at' => null,
        ];
    }

    private function extract(Request $request): array
    {
        $status = Filter::in($request->input('status'), ['published', 'draft'], 'draft');
        $bodyFormat = Filter::in($request->input('body_format'), ['markdown', 'html'], 'html');
        $bodyRaw = Filter::string($request->input('body'), 65535 * 4);
        $body = $bodyFormat === 'html' ? HtmlSanitizer::clean($bodyRaw) : $bodyRaw;
        return [
            'title' => Filter::string($request->input('title'), 255),
            'slug' => Filter::urlSlug($request->input('slug', '')),
            'body' => $body,
            'body_format' => $bodyFormat,
            'status' => $status,
            'seo_title' => Filter::string($request->input('seo_title'), 255),
            'seo_description' => Filter::string($request->input('seo_description'), 65535),
            'og_image' => Filter::string($request->input('og_image'), 500),
            'published_at' => null,
        ];
    }

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