<?php

use Acms\Services\Facades\Application;
use Acms\Services\Backup\StorageDirPolicy;

class ACMS_POST_Backup_ArchiveExport extends ACMS_POST_Backup_Base
{
    /**
     * @var string
     */
    protected $lockFile;

    public function post()
    {
        try {
            if (env('STORAGE_DRIVER', 'local') !== 'local') {
                throw new \RuntimeException(gettext('ストレージ設定がローカルではない（S3など）ため、バックアップできません。'));
            }
            AcmsLogger::info('アーカイブのエクスポートを実行しました');

            $this->authCheck('backup_export');
            ignore_user_abort(true);
            set_time_limit(0);
            $lockService = Application::make('archive.backup-lock');

            // リダイレクトより前にロックを取得する（理由は ACMS_POST_Backup_Export と同じ）
            if (!$lockService->tryLock()) {
                throw new \RuntimeException(gettext('アーカイブのバックアップを中止しました。すでにバックアップ中の可能性があります。変化がない場合は、cache/archives-backup-lock ファイルを削除してお試しください。'));
            }
            Response::backgroundRedirect(HTTP_REQUEST_URL);
            $this->run($lockService);
            die();
        } catch (\Exception $e) {
            $this->addError($e->getMessage());
            AcmsLogger::warning('アーカイブのバックアップに失敗しました', Common::exceptionArray($e));
        }
        return $this->Post;
    }

    protected function run(\Acms\Services\Common\Lock $lockService)
    {
        set_time_limit(0);
        $logger = App::make('archives.logger');

        DB::setThrowException(true);
        try {
            $lockService->tryLock();
            $logger->init();
            LocalStorage::makeDirectory($this->backupArchivesDir);
            $dest = $this->backupArchivesDir . 'archives' . date('_Ymd_Hi') . '.zip';

            $logger->addMessage('archives をバックアップ中...', 5);
            LocalStorage::compress(ARCHIVES_DIR, $dest, 'archives_tmp/archives');
            $logger->addMessage('archives のバックアップ完了', 25);

            $logger->addMessage('media をバックアップ中...', 5);
            LocalStorage::compress(MEDIA_LIBRARY_DIR, $dest, 'archives_tmp/media');
            $logger->addMessage('media のバックアップ完了', 25);

            $logger->addMessage('storage をバックアップ中...', 5);
            // Why not: 対象外にしたディレクトリを黙って捨てない。許可リストは既定拒否のため、
            // 想定外の名前で置かれたメディアファイルがバックアップから静かに落ちる余地がある。
            // 落としたものをログに残し、復元時に気づけるようにする。
            $skippedDirs = [];
            $storageTarget = MEDIA_STORAGE_DIR;
            if ($dir = opendir($storageTarget)) {
                while (($file = readdir($dir)) !== false) {
                    if ($file != "." && $file != ".." && substr($file, 0, 1) !== '.') {
                        // Why not: バックアップ出力先を除外する方式（除外リスト）に戻さない。
                        // 判定理由は Acms\Services\Backup\StorageDirPolicy の docblock を参照。
                        if (!StorageDirPolicy::isBackupTargetDirName($file)) {
                            $skippedDirs[] = $file;
                            continue;
                        }
                        LocalStorage::compress(
                            MEDIA_STORAGE_DIR . $file,
                            $dest,
                            'archives_tmp/storage/' . $file
                        );
                    }
                }
                closedir($dir);
            }
            if ($skippedDirs !== []) {
                $logger->addMessage(
                    'storage の対象外ディレクトリを除きました: ' . implode(', ', $skippedDirs),
                    0
                );
            }
            $logger->addMessage('storage のバックアップ完了', 25);

            $logger->addMessage('バックアップ完了', 10);
            $logger->success();
        } catch (\Exception $e) {
            if ($message = $e->getMessage()) {
                $logger->error($message);

                AcmsLogger::warning('アーカイブのバックアップ中にエラーが発生しました。', Common::exceptionArray($e));
            }
        } finally {
            $lockService->release();
            sleep(3);
            $logger->terminate();
        }
        DB::setThrowException(false);
    }
}
