<?php
/// file: app/Controllers/Admin/MediaAdminController.php

declare(strict_types=1);

namespace App\Controllers\Admin;

use App\Core\Controller;
use App\Core\Csrf;
use App\Core\Request;
use App\Core\Response;
use App\Core\Session;
use App\Models\SiteSetting;
use PDO;

final class MediaAdminController extends Controller
{
    private const MAX_BYTES = 14 * 1024 * 1024;

    /** @var array<string, string> */
    private const IMAGE_EXT = [
        'image/jpeg' => 'jpg',
        'image/png' => 'png',
        'image/webp' => 'webp',
        'image/gif' => 'gif',
    ];

    /** @var array<string, string> */
    private const VIDEO_EXT = [
        'video/mp4' => 'mp4',
        'video/webm' => 'webm',
    ];

    public function index(Request $request): Response
    {
        $pdo = $this->app->pdo();
        $projects = $pdo->query(
            'SELECT id, title, slug, cover_path, cover_type FROM projects ORDER BY sort_order ASC, id DESC'
        )->fetchAll(PDO::FETCH_ASSOC) ?: [];
        $journal = $pdo->query(
            'SELECT id, title, slug, cover_path FROM journal_posts ORDER BY id DESC'
        )->fetchAll(PDO::FETCH_ASSOC) ?: [];
        $seo = $pdo->query(
            'SELECT page_key, og_image FROM seo_meta ORDER BY page_key ASC'
        )->fetchAll(PDO::FETCH_ASSOC) ?: [];
        $settings = SiteSetting::allAssoc();
        $flash = Session::pullFlash();
        $html = $this->adminView('admin/media/index', [
            'projects' => $projects,
            'journal' => $journal,
            'seo' => $seo,
            'settings' => $settings,
            'flash' => $flash,
        ]);

        return Response::html($html);
    }

    public function saveGlobal(Request $request): Response
    {
        if (! Csrf::verify($request->input('_csrf'))) {
            return $this->redirect(url('/admin/media'));
        }
        $og = trim((string) $request->input('og_default_image', ''));
        $ph = trim((string) $request->input('default_placeholder_image', ''));
        $upOg = $this->storeUpload('upload_og', false);
        if ($upOg !== null) {
            $og = $upOg;
        }
        $upPh = $this->storeUpload('upload_placeholder', false);
        if ($upPh !== null) {
            $ph = $upPh;
        }
        SiteSetting::set('og_default_image', $og);
        SiteSetting::set('default_placeholder_image', $ph);
        Session::flash('media_ok', 'Global images saved.');

        return $this->redirect(url('/admin/media'));
    }

    public function saveProject(Request $request): Response
    {
        if (! Csrf::verify($request->input('_csrf'))) {
            return $this->redirect(url('/admin/media'));
        }
        $id = (int) ($request->param('id') ?? 0);
        $pdo = $this->app->pdo();
        $st = $pdo->prepare('SELECT id FROM projects WHERE id = ? LIMIT 1');
        $st->execute([$id]);
        if (! $st->fetch()) {
            return $this->redirect(url('/admin/media'));
        }
        $path = trim((string) $request->input('cover_path', ''));
        $coverType = $request->input('cover_type', 'image') === 'video' ? 'video' : 'image';
        $up = $this->storeUpload('upload', true);
        if ($up !== null) {
            $path = $up['path'];
            $coverType = $up['kind'];
        }
        $pdo->prepare('UPDATE projects SET cover_path = ?, cover_type = ? WHERE id = ?')->execute([$path, $coverType, $id]);
        Session::flash('media_ok', 'Project cover updated.');

        return $this->redirect(url('/admin/media'));
    }

    public function saveJournal(Request $request): Response
    {
        if (! Csrf::verify($request->input('_csrf'))) {
            return $this->redirect(url('/admin/media'));
        }
        $id = (int) ($request->param('id') ?? 0);
        $pdo = $this->app->pdo();
        $st = $pdo->prepare('SELECT id FROM journal_posts WHERE id = ? LIMIT 1');
        $st->execute([$id]);
        if (! $st->fetch()) {
            return $this->redirect(url('/admin/media'));
        }
        $path = trim((string) $request->input('cover_path', ''));
        $up = $this->storeUpload('upload', false);
        if ($up !== null) {
            $path = is_array($up) ? $up['path'] : $up;
        }
        $pdo->prepare('UPDATE journal_posts SET cover_path = ? WHERE id = ?')->execute([$path, $id]);
        Session::flash('media_ok', 'Journal cover updated.');

        return $this->redirect(url('/admin/media'));
    }

    public function saveSeo(Request $request): Response
    {
        if (! Csrf::verify($request->input('_csrf'))) {
            return $this->redirect(url('/admin/media'));
        }
        $pageKey = trim((string) $request->input('page_key', ''));
        if ($pageKey === '') {
            return $this->redirect(url('/admin/media'));
        }
        $pdo = $this->app->pdo();
        $st = $pdo->prepare('SELECT page_key FROM seo_meta WHERE page_key = ? LIMIT 1');
        $st->execute([$pageKey]);
        if (! $st->fetch()) {
            return $this->redirect(url('/admin/media'));
        }
        $path = trim((string) $request->input('og_image', ''));
        $up = $this->storeUpload('upload', false);
        if ($up !== null) {
            $path = is_array($up) ? $up['path'] : $up;
        }
        $pdo->prepare('UPDATE seo_meta SET og_image = ? WHERE page_key = ?')->execute([$path, $pageKey]);
        Session::flash('media_ok', 'SEO image updated.');

        return $this->redirect(url('/admin/media'));
    }

    /**
     * @return array{path:string, kind:string}|string|null null = no file; string path for image-only; array for project with type
     */
    private function storeUpload(string $field, bool $allowVideo): mixed
    {
        if (! isset($_FILES[$field]) || ! is_array($_FILES[$field])) {
            return null;
        }
        $err = (int) ($_FILES[$field]['error'] ?? UPLOAD_ERR_NO_FILE);
        if ($err === UPLOAD_ERR_NO_FILE) {
            return null;
        }
        if ($err !== UPLOAD_ERR_OK) {
            return null;
        }
        $tmp = (string) ($_FILES[$field]['tmp_name'] ?? '');
        $size = (int) ($_FILES[$field]['size'] ?? 0);
        if ($tmp === '' || ! is_uploaded_file($tmp) || $size < 1 || $size > self::MAX_BYTES) {
            return null;
        }
        $finfo = new \finfo(FILEINFO_MIME_TYPE);
        $mime = $finfo->file($tmp);
        if (! is_string($mime)) {
            return null;
        }
        $ext = self::IMAGE_EXT[$mime] ?? null;
        $kind = 'image';
        if ($ext === null && $allowVideo) {
            $ext = self::VIDEO_EXT[$mime] ?? null;
            if ($ext !== null) {
                $kind = 'video';
            }
        }
        if ($ext === null) {
            return null;
        }
        $dir = $this->app->basePath() . '/public/assets/uploads';
        if (! is_dir($dir) && ! mkdir($dir, 0755, true)) {
            return null;
        }
        $name = bin2hex(random_bytes(12)) . '.' . $ext;
        $dest = $dir . '/' . $name;
        if (! move_uploaded_file($tmp, $dest)) {
            return null;
        }
        $publicPath = '/assets/uploads/' . $name;
        if ($allowVideo) {
            return ['path' => $publicPath, 'kind' => $kind];
        }

        return $publicPath;
    }
}
