<?php

namespace Shm\ShmBlueprints\FileUpload\Utils;

use Exception;
use Shm\ShmBlueprints\FileUpload\ShmFileUploadUtils;
use Shm\ShmDB\mDB;
use Shm\ShmUtils\ShmInit;

class ShmVideoResize
{
    private $input;
    private $output;
    private $unicFileName;
    private $originalUrl;
    public $resizeUrl;
    private $_id;

    public static function findVideoForResize()
    {
        $file = mDB::collection("_files")->findOne([
            "type" => "video/mp4",
            "resized" => ['$ne' => true]
        ]);

        if (!$file) {

            throw new \Exception('Video for resize not found');
        }

        return new self($file->_id);
    }

    public function __construct($_id)
    {

        $this->_id = mDB::id($_id);

        $file = mDB::collection("_files")->findOne([
            "type" => "video/mp4",
            "_id" => mDB::id($_id)
        ]);

        if (!$file) {
            throw new \Exception('File not found');
        }

        $this->originalUrl = $file->url_original ?? $file->url ?? null;

        if (!$this->originalUrl) {
            throw new \Exception('URL not found');
        }

        try {

            $content = file_get_contents($this->originalUrl);
        } catch (Exception $e) {
            throw new \Exception('Error file_get_contents: ' . $e->getMessage());
        }

        $this->unicFileName = uniqid();

        $tmpDir = ShmInit::$rootDir . '/storage/tmp/';

        if (!is_dir($tmpDir)) {
            mkdir($tmpDir, 0777, true);
        }



        $this->input  =  $tmpDir . $this->unicFileName . '_input.mp4';
        $this->output =   $tmpDir  . $this->unicFileName . '_output.mp4';

        try {
            file_put_contents($this->input, $content);
        } catch (Exception $e) {

            unlink($this->input);


            throw new \Exception('Error file_put_contents: ' . $e->getMessage());
        }
    }

    public function resize($preset = 'fast'): self
    {

        $allPresets = [
            'ultrafast',
            'superfast',
            'veryfast',
            'faster',
            'fast',
            'medium',
            'slow',
            'slower',
            'veryslow',
        ];

        if (!in_array($preset, $allPresets)) {
            throw new \Exception('Invalid preset: ' . $preset);
        }

        $cmd = "ffmpeg -i $this->input "
            // -i input.mp4
            // Входной файл
            // Можно: любой формат, который ffmpeg понимает

            . "-c:v libx264 "
            // Видеокодек H.264 (x264)
            // Совместим почти везде
            // Если сменить на libx265 → меньше размер, хуже совместимость

            . "-preset $preset "
            // Скорость кодирования vs качество
            // ultrafast → быстро, хуже качество
            // slow / veryslow → лучше качество, дольше кодирует
            // fast — нормальный компромисс

            . "-crf 32 "
            // Constant Rate Factor (качество видео)
            // Меньше = лучше качество = больше размер
            // 18–23 → высокое качество
            // 24–28 → среднее
            // 30–35 → сильное сжатие
            // 32 → осознанно экономишь размер

            . "-r 25 "
            // FPS (кадры в секунду)
            // 25 → стандарт PAL / web
            // 30 → плавнее, больше размер
            // Лучше не повышать без причины

            . "-g 50 "
            // GOP size (keyframe каждые 50 кадров)
            // При 25 fps → ключевой кадр раз в 2 секунды
            // Меньше → лучше перемотка, больше размер
            // Больше → хуже перемотка, меньше размер

            . "-keyint_min 25 "
            // Минимальное расстояние между keyframe
            // Не даёт кодеку ставить их слишком часто
            // Обычно = fps

            . "-maxrate 1200k "
            // Максимальный битрейт видео
            // Ограничивает пики
            // Меньше → меньше размер, больше артефактов
            // Больше → стабильнее картинка

            . "-bufsize 2400k "
            // Размер буфера (обычно 2x maxrate)
            // Чем больше → сглаживает скачки битрейта
            // Слишком маленький → рывки качества

            . "-profile:v main "
            // H.264 профиль
            // baseline → старые устройства
            // main → нормальные устройства
            // high → лучше сжатие, хуже совместимость

            . "-pix_fmt yuv420p "
            // Цветовой формат
            // yuv420p → обязателен для браузеров
            // 422 / 444 → больше цвет, меньше поддержка

            . "-x264-params \"ref=3:bframes=2:psy=0\" "
            // ref=3 → количество reference frames
            // bframes=2 → количество B-кадров
            // psy=0 → отключает психо-визуальные искажения
            // Менять осторожно, это тонкая настройка

            . "-map_metadata -1 "
            // Удаляет все метаданные
            // Автор, устройство, GPS и т.д.
            // Полезно для приватности и размера

            . "-c:a aac "
            // Аудиокодек AAC
            // Лучший выбор для web
            // mp3 → хуже качество при том же битрейте

            . "-ac 1 "
            // Количество аудиоканалов
            // 1 → моно (экономия размера)
            // 2 → стерео (лучше музыка)

            . "-b:a 128k "
            // Битрейт аудио
            // 32–48k → речь
            // 64–96k → норм
            // 40k → агрессивная экономия

            //. "-af deesser=i=0.45:f=0.25:l=0.4,loudnorm=I=-16:LRA=7:TP=-1.5 "
            // . "-af deesser=i=0.45:f=6000:l=0.4,loudnorm=I=-16:LRA=7:TP=-1.5 "
            . "-af loudnorm=I=-16:LRA=7:TP=-1.5 "
            // Нормализация громкости (EBU R128)
            // I=-16 → целевая громкость для web
            // LRA=7 → сжатая динамика (под речь)
            // TP=-1.5 → защита от клиппинга
            // LRA ↑ → больше динамики, LRA ↓ → плотнее звук

            . "-movflags +faststart "
            // Переносит moov-atom в начало файла
            // Видео начинает играть до полной загрузки
            // Крайне желательно для web

            . "$this->output 2>&1";

        exec($cmd, $outputLog, $returnCode);


        if ($returnCode !== 0) {

            unlink($this->output);
            unlink($this->input);

            throw new \Exception('Error ffmpeg: ' . implode("\n", $outputLog));
        }

        return $this;
    }

    public function cleanTmpFiles()
    {
        unlink($this->output);
        unlink($this->input);
    }

    public function saveResizeFileS3(): self
    {
        $resizeUrl = ShmFileUploadUtils::saveToS3($this->output, $this->unicFileName . '_output.mp4', "videos");

        $this->resizeUrl = $resizeUrl;

        return $this;
    }

    public function updateFileDB()
    {

        if (!$this->resizeUrl) {
            throw new \Exception('Resize URL not found');
        }

        if (!$this->originalUrl) {
            throw new \Exception('Original URL not found');
        }

        mDB::collection("_files")->updateOne([
            "_id" => $this->_id
        ], [
            '$set' => [
                "url" => $this->resizeUrl,
                "resized" => true,
                "url_original" => $this->originalUrl,
            ]
        ]);

        $this->cleanTmpFiles();
    }
}