<?php

namespace Acms\Services\Form;

use Acms\Services\Facades\Application as App;
use Acms\Services\Auth\Contracts\Guard;

/**
 * フォーム機能の認可判定を集約する。
 *
 * ロール運用時は form_view / form_edit ロール、未使用時はコンフィグ設定
 * form_admin_authority に応じた編集者権限・管理者権限で判定する。
 * 各ハンドラ（ACMS_POST_Form_* / ACMS_GET_Admin_Form_*）から共通利用する。
 *
 * 判定対象のユーザーは引数で受け取り、テスト時に任意のユーザーを差し込めるようにする。
 * ロール運用時はロールガードを直接利用し（Auth ファサードはガード選択がセッション
 * ユーザー（SUID）基準になるため）、引数で受け取ったユーザーを基準に評価する。
 * ロール未使用時は sessionWithFormAdministration / sessionWithAdministration の
 * 第3引数 userId に引数のユーザーを渡して評価する（cache は無効化し、判定対象
 * ユーザーが呼び出しごとに変わることを反映する）。sessionWith* 側が
 * isPermissionOfEditor・isPermissionOfAdministrator・timemachineMode の判定を
 * まとめて持つため、ここで同じ判定ロジックを再実装せずに済み、将来 sessionWith*
 * 側にガードが追加された場合も自動的に追従できる
 * （{@see \Acms\Services\ConfigSet\Authorization} と同じ設計方針）。
 */
class Authorization
{
    /**
     * フォームの閲覧・エクスポートが許可されているか
     *
     * @param int $userId 判定対象のユーザーID（通常はログイン中のユーザー SUID）
     * @param int $blogId 対象ブログID
     * @return bool
     */
    public function canView(int $userId, int $blogId): bool
    {
        if ($userId < 1) {
            return false;
        }

        if (roleAvailableUser($userId)) {
            $guard = $this->roleGuard();
            return $guard->roleAuthorization('form_view', $blogId, 0, $userId)
                || $guard->roleAuthorization('form_edit', $blogId, 0, $userId);
        }

        return sessionWithFormAdministration($blogId, ['cache' => false], $userId);
    }

    /**
     * フォームの編集・作成・削除・複製・インポートが許可されているか
     *
     * @param int $userId 判定対象のユーザーID（通常はログイン中のユーザー SUID）
     * @param int $blogId 対象ブログID
     * @return bool
     */
    public function canEdit(int $userId, int $blogId): bool
    {
        if ($userId < 1) {
            return false;
        }

        if (roleAvailableUser($userId)) {
            return $this->roleGuard()->roleAuthorization('form_edit', $blogId, 0, $userId);
        }

        return sessionWithFormAdministration($blogId, ['cache' => false], $userId);
    }

    /**
     * フォームの投稿ログ閲覧が許可されているか
     *
     * 閲覧系（canView）と異なり、ロール運用時は form_view 権限のみで判定する
     * （form_edit だけのユーザーにはログを開放しない）。
     *
     * @param int $userId 判定対象のユーザーID（通常はログイン中のユーザー SUID）
     * @param int $blogId 対象ブログID
     * @return bool
     */
    public function canViewLog(int $userId, int $blogId): bool
    {
        if ($userId < 1) {
            return false;
        }

        if (roleAvailableUser($userId)) {
            return $this->roleGuard()->roleAuthorization('form_view', $blogId, 0, $userId);
        }

        return sessionWithFormAdministration($blogId, ['cache' => false], $userId);
    }

    /**
     * フォームの投稿ログ削除が許可されているか
     *
     * 編集系（canEdit）と異なり、ロール未使用時はフォーム管理権限ではなく
     * ブログ管理者権限（sessionWithAdministration）を要求する。
     *
     * @param int $userId 判定対象のユーザーID（通常はログイン中のユーザー SUID）
     * @param int $blogId 対象ブログID
     * @return bool
     */
    public function canDeleteLog(int $userId, int $blogId): bool
    {
        if ($userId < 1) {
            return false;
        }

        if (roleAvailableUser($userId)) {
            return $this->roleGuard()->roleAuthorization('form_edit', $blogId, 0, $userId);
        }

        return sessionWithAdministration($blogId, ['cache' => false], $userId);
    }

    /**
     * ロールベースの権限判定ガードを取得する。
     *
     * Auth ファサードはガード選択をセッションユーザー（SUID）基準で行うため、
     * 引数で受け取ったユーザーを基準に判定するにはロールガードを直接利用する。
     * roleAvailableUser() が真のユーザーに対してのみ呼ばれるため、本番では
     * Auth ファサード経由（ロールユーザーは同じくロールガードに振られる）と同一の結果になる。
     *
     * @return \Acms\Services\Auth\Contracts\Guard
     */
    private function roleGuard(): Guard
    {
        $guard = App::make('auth.role');
        assert($guard instanceof Guard);

        return $guard;
    }
}
