<?php

use Acms\Services\Image\WebpConvertRepository;

class ACMS_POST_Fix_Webp extends ACMS_POST_Fix
{
    public function post()
    {
        @set_time_limit(0);
        // WebP変換はサイト内の全画像（media・archives 全体）に影響するため、子ブログ管理者の
        // BID では通過させずルートブログ限定にする。BID を渡すと isControlBlog() が自ブログと
        // 配下を許してしまうので、RBID を渡して「ルートブログの管理者か」を判定する。
        if (!sessionWithAdministration(RBID)) {
            return $this->Post;
        }

        $lockService = App::make('image.webp-convert-lock');
        $repository = new WebpConvertRepository([
            MEDIA_LIBRARY_DIR,
            ARCHIVES_DIR,
        ]);

        // 実行前チェックの失敗は例外にせず message をセットして返す。バックグラウンド処理の
        // 開始前なので進捗JSON経由でUIに出す経路がなく、テンプレートの message# ブロックが
        // 唯一の伝達手段になるため。
        if (!$repository->isWebpSupported()) {
            AcmsLogger::warning('WebP変換を中止しました。WebP画像がサポートされていない環境です。');
            $this->Post->set('message', 'unsupported_error');
            return $this->Post;
        }
        // ロックと進捗ログの初期化は backgroundRedirect より前に済ませる。run() の中で取ると、
        // リダイレクトを追ったGETがロック取得より先に到達し、実行中にもかかわらず進捗画面へ
        // 切り替わらない。isLocked() での事前確認ではなく tryLock() の戻り値で判定することで、
        // 確認から取得までの隙間もなくす。
        if (!$lockService->tryLock()) {
            AcmsLogger::warning('WebP変換を中止しました。すでに実行中の可能性があります。');
            $this->Post->set('message', 'locked_error');
            return $this->Post;
        }

        try {
            $logger = App::make('common.logger');
            $logger->setDestinationPath(CACHE_DIR . 'webp-convert-logger.json');
            $logger->init();
            $logger->addMessage('対象ファイルを検索中...', 0);

            Response::backgroundRedirect(HTTP_REQUEST_URL);
            $this->run($lockService, $repository, $logger);
            die();
        } catch (\Throwable $e) {
            $lockService->release();
            AcmsLogger::warning('WebP変換の開始に失敗しました', Common::exceptionArray($e));
            $this->Post->set('message', 'error');
        }

        return $this->Post;
    }

    /**
     * WebP変換の実処理
     *
     * バックグラウンド処理基盤（Common::backgroundRedirect）でクライアントへの
     * レスポンスを返した後、同一プロセス内で継続実行される長時間処理。
     * ロックの取得と進捗ログの初期化は post() 側で済ませてある。
     *
     * @param \Acms\Services\Common\Lock $lockService
     * @param WebpConvertRepository $repository
     * @param \Acms\Services\Common\Logger $logger
     * @return void
     */
    public function run(
        \Acms\Services\Common\Lock $lockService,
        WebpConvertRepository $repository,
        \Acms\Services\Common\Logger $logger
    ): void {
        set_time_limit(0);

        try {
            $targets = $repository->collectTargetFiles();
        } catch (\Throwable $e) {
            $logger->error($e->getMessage());
            $lockService->release();
            sleep(5);
            $logger->terminate();

            AcmsLogger::warning('WebP変換でエラーが発生しました', Common::exceptionArray($e));
            return;
        }

        $count = count($targets);
        $successCount = 0;
        $errorCount = 0;

        if ($count === 0) {
            $logger->addMessage('WebP変換の対象ファイルはありませんでした', 100);
        } else {
            $increase = 90 / $count;
            $logger->addMessage('WebP変換中...', 0);

            foreach ($targets as $i => $path) {
                $logger->addMessage(($i + 1) . " / {$count}", $increase, 1, false);
                try {
                    $repository->convert($path);
                    $successCount++;
                } catch (\Throwable $e) {
                    $errorCount++;
                    $logger->addProcessLog($path . ': ' . $e->getMessage(), 0);

                    AcmsLogger::notice('WebP変換に失敗したためこのファイルはスキップします: ' . $path, Common::exceptionArray($e));
                }
            }

            $logger->addMessage('WebP変換完了', 100);
            $logger->addProcessLog('生成成功件数: ' . $successCount . '件');
            $logger->addProcessLog('生成失敗件数: ' . $errorCount . '件');
        }

        $logger->success();

        AcmsLogger::info('データ修正ツールで、WebP変換を実行しました', [
            'success' => $successCount,
            'error' => $errorCount,
        ]);

        $lockService->release();
        sleep(5);
        $logger->terminate();
    }
}
