<?php
declare(strict_types=1);

namespace Celena\Module\Media;

use Celena\Core\Database\Connection;
use Celena\Core\Http\Pagination;
use Celena\Core\Http\Request;
use Celena\Core\Http\Response;
use Celena\Core\Kernel;
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;

final class MediaController extends AdminController
{
    private const ALLOWED_MIME = [
        'image/jpeg' => 'jpg',
        'image/png'  => 'png',
        'image/gif'  => 'gif',
        'image/webp' => 'webp',
        'application/pdf' => 'pdf',
    ];
    private const MAX_SIZE = 20 * 1024 * 1024; // 20 MB

    private static function safeOriginalName(string $name): string
    {
        // вырезаем управляющие символы и HTML-чувствительные, оставляем только безопасный набор
        $name = preg_replace('/[\x00-\x1F\x7F<>"\'\\\\]/u', '', $name) ?? '';
        $name = trim($name);
        return mb_substr($name, 0, 255, 'UTF-8');
    }

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

    private function imageProcessor(): \Celena\Core\Support\ImageProcessor
    {
        return new \Celena\Core\Support\ImageProcessor(
            maxOriginal: max(200, (int) $this->options->get('media_max_original', 2400)),
            jpegQuality: (int) max(10, min(100, (int) $this->options->get('media_jpeg_quality', 82))),
            webpQuality: (int) max(10, min(100, (int) $this->options->get('media_webp_quality', 80))),
            thumbSize:   max(64, (int) $this->options->get('media_thumb_size', 320)),
            mediumSize:  max(100, (int) $this->options->get('media_medium_size', 800)),
            largeSize:   max(200, (int) $this->options->get('media_large_size', 1600)),
        );
    }

    public function index(Request $request): Response
    {
        if (!Permission::can($this->auth->user(), 'media.read')) {
            return $this->redirect('/admin', 'Доступ запрещён.', 'error');
        }
        $page = Filter::int($request->query('page', 1), 1);
        $total = $this->conn->builder('celena_media')->count();
        $p = new Pagination($page, 40, $total);
        $rows = $this->conn->builder('celena_media')
            ->orderBy('id', 'desc')
            ->limit($p->perPage)->offset($p->offset)
            ->get();
        return $this->view('admin/media/index', [
            'media' => $rows,
            'pagination' => $p->toArray('/admin/media?page=:page'),
        ], 'media', 'Медиа');
    }

    public function upload(Request $request): Response
    {
        if (!Permission::can($this->auth->user(), 'media.upload')) {
            return Response::json(['error' => 'forbidden'], 403);
        }
        if (!Csrf::validate((string) ($request->header('x-csrf-token') ?? $request->input('_csrf')))) {
            return Response::json(['error' => 'csrf'], 403);
        }
        $file = $request->file('file');
        if ($file === null || ($file['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) {
            return Response::json(['error' => 'no_file'], 400);
        }
        if (($file['size'] ?? 0) > self::MAX_SIZE) {
            return Response::json(['error' => 'too_large'], 400);
        }
        $tmp = (string) $file['tmp_name'];
        if (!is_uploaded_file($tmp)) {
            return Response::json(['error' => 'invalid_upload'], 400);
        }
        $mime = (string) (mime_content_type($tmp) ?: $file['type'] ?? '');
        if (!isset(self::ALLOWED_MIME[$mime])) {
            return Response::json(['error' => 'unsupported_type', 'mime' => $mime], 400);
        }
        $ext = self::ALLOWED_MIME[$mime];
        $uuid = bin2hex(random_bytes(8));
        $sub = date('Y/m');
        $dir = Kernel::rootPath() . '/public/uploads/' . $sub;
        if (!is_dir($dir)) {
            @mkdir($dir, 0755, true);
        }
        $filename = $uuid . '.' . $ext;
        $target = $dir . '/' . $filename;
        if (!move_uploaded_file($tmp, $target)) {
            return Response::json(['error' => 'move_failed'], 500);
        }
        [$width, $height] = @getimagesize($target) ?: [null, null];

        $url = '/public/uploads/' . $sub . '/' . $filename;

        // Ресайз + варианты для изображений
        $variants = [];
        if (\Celena\Core\Support\ImageProcessor::isImage($mime)) {
            try {
                $proc = $this->imageProcessor();
                $result = $proc->process($target, $url);
                $width = $result['width'];
                $height = $result['height'];
                $variants = $result['variants'];
                // Обновим размер файла после сжатия
                clearstatcache(true, $target);
            } catch (\Throwable) {
                // не критично — оригинал остался
            }
        }

        $id = $this->conn->builder('celena_media')->insert([
            'filename' => $filename,
            'original_name' => self::safeOriginalName((string) ($file['name'] ?? $filename)),
            'mime' => $mime,
            'size' => (int) (filesize($target) ?: $file['size']),
            'width' => $width,
            'height' => $height,
            'path' => $target,
            'url' => $url,
            'uploaded_by' => $this->auth->id(),
            'created_at' => date('Y-m-d H:i:s'),
        ]);

        return Response::json([
            'ok' => true,
            'id' => (int) $id,
            'url' => $url,
            'mime' => $mime,
            'width' => $width,
            'height' => $height,
            'variants' => $variants,
        ]);
    }

    /**
     * POST /admin/media/upload-images — multi-upload для celenaEditor.
     * Принимает files[] (multipart), грузит каждый через обычный процесс (с ресайзом),
     * возвращает HTML с <img> тегами (редактор просто подставляет его в тело).
     */
    public function uploadImages(Request $request): Response
    {
        if (!Permission::can($this->auth->user(), 'media.upload')) {
            return Response::html('', 403);
        }
        $token = (string) (
            $request->input('csrf_token')
            ?? $request->header('x-csrf-token')
            ?? $request->input('_csrf')
            ?? ''
        );
        if (!Csrf::validate(rawurldecode($token))) {
            return Response::html('<!-- csrf -->', 403);
        }
        $files = $request->files['files'] ?? null;
        if (!is_array($files) || !isset($files['tmp_name'])) {
            return Response::html('', 400);
        }
        $names = (array) $files['tmp_name'];
        $count = count($names);
        $html = '';
        for ($i = 0; $i < $count; $i++) {
            if (($files['error'][$i] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) continue;
            if (($files['size'][$i] ?? 0) > 20 * 1024 * 1024) continue;
            $tmp = (string) $files['tmp_name'][$i];
            if (!is_uploaded_file($tmp)) continue;
            $mime = (string) (mime_content_type($tmp) ?: $files['type'][$i] ?? '');
            $allowed = ['image/jpeg'=>'jpg','image/png'=>'png','image/gif'=>'gif','image/webp'=>'webp'];
            if (!isset($allowed[$mime])) continue;
            $ext = $allowed[$mime];
            $sub = date('Y/m');
            $dir = \Celena\Core\Kernel::rootPath() . '/public/uploads/' . $sub;
            if (!is_dir($dir)) @mkdir($dir, 0755, true);
            $filename = bin2hex(random_bytes(8)) . '.' . $ext;
            $target = $dir . '/' . $filename;
            if (!move_uploaded_file($tmp, $target)) continue;
            $url = '/public/uploads/' . $sub . '/' . $filename;
            $w = null; $h = null;
            try {
                $proc = $this->imageProcessor();
                $res = $proc->process($target, $url);
                $w = $res['width'] ?? null;
                $h = $res['height'] ?? null;
            } catch (\Throwable) { /* оставляем оригинал */ }
            if ($w === null || $h === null) {
                $info = @getimagesize($target);
                if ($info !== false) { $w = $info[0]; $h = $info[1]; }
            }
            clearstatcache(true, $target);
            $this->conn->builder('celena_media')->insert([
                'filename' => $filename,
                'original_name' => self::safeOriginalName((string) ($files['name'][$i] ?? $filename)),
                'mime' => $mime,
                'size' => (int) (filesize($target) ?: 0),
                'width' => $w,
                'height' => $h,
                'path' => $target,
                'url' => $url,
                'uploaded_by' => $this->auth->id(),
                'created_at' => date('Y-m-d H:i:s'),
            ]);
            $html .= '<img src="' . htmlspecialchars($url, ENT_QUOTES) . '" alt="">';
        }
        return Response::html($html ?: '<!-- no images -->');
    }

    /** API: список медиафайлов только изображений (для picker'а). */
    public function pick(Request $request): Response
    {
        if (!Permission::can($this->auth->user(), 'media.read')) {
            return Response::json(['error' => 'forbidden'], 403);
        }
        $page = Filter::int($request->query('page', 1), 1);
        $perPage = 40;
        $rows = $this->conn->builder('celena_media')
            ->whereRaw($this->conn->quoteId('mime') . " LIKE 'image/%'")
            ->orderBy('id', 'desc')
            ->limit($perPage)->offset(($page - 1) * $perPage)
            ->get();
        $items = array_map(fn ($r) => [
            'id' => (int) $r['id'],
            'url' => (string) $r['url'],
            'name' => (string) $r['original_name'],
            'width' => (int) ($r['width'] ?? 0),
            'height' => (int) ($r['height'] ?? 0),
        ], $rows);
        return Response::json(['items' => $items, 'page' => $page]);
    }

    public function delete(Request $request, array $params): Response
    {
        if (!Permission::can($this->auth->user(), 'media.delete')) {
            return $this->redirect('/admin/media', 'Доступ запрещён.', 'error');
        }
        if (!Csrf::validate((string) $request->input('_csrf'))) {
            return $this->redirect('/admin/media', 'CSRF.', 'error');
        }
        $id = Filter::int($params['id'] ?? 0);
        $row = $this->conn->builder('celena_media')->where('id', '=', $id)->first();
        if ($row !== null) {
            @unlink((string) $row['path']);
            $this->conn->builder('celena_media')->where('id', '=', $id)->delete();
        }
        return $this->redirect('/admin/media', 'Файл удалён.');
    }
}
