<?php

declare(strict_types=1);

namespace Acms\Services\Validator;

use ACMS_RAM;
use ACMS_Validator;
use Acms\Services\Blog\BlogRepository;
use Acms\Services\Category\CategoryRepository;
use Acms\Services\Facades\Application as App;

/**
 * カテゴリーの入力バリデーション
 *
 * `Field_Validation` から `setMethod('code', 'double', [...])` などの形式で呼び出される。
 *
 * 操作対象を表すリクエスト定数（CID 等）に依存する検証はここに置かず、
 * 呼び出し元（POST ハンドラ）で Repository を直接利用すること。
 * 一方、BID（リクエスト対象ブログ）は a-blog cms の慣例として
 * すべてのブログスコープ操作で暗黙のコンテキストとなるため、ここで参照してよい。
 */
class Category extends ACMS_Validator
{
    private function repository(): CategoryRepository
    {
        /** @var CategoryRepository $repository */
        $repository = App::make('category.repository');
        return $repository;
    }

    private function blogRepository(): BlogRepository
    {
        /** @var BlogRepository $repository */
        $repository = App::make('blog.repository');
        return $repository;
    }

    /**
     * カテゴリーコードの重複チェック
     *
     * @param mixed $ccd 入力されたカテゴリーコード
     * @param array<int, mixed> $arg [scope, parentCategoryId, selfCategoryId]
     */
    public function double($ccd, array $arg = []): bool
    {
        $code = is_scalar($ccd) ? (string) $ccd : '';
        if ($code === '') {
            return true;
        }

        $scope = isset($arg[0]) ? (string) $arg[0] : 'local';
        $parentCategoryId = isset($arg[1]) ? (int) $arg[1] : 0;
        $selfCategoryIdRaw = isset($arg[2]) ? (int) $arg[2] : 0;
        $excludeCategoryId = $selfCategoryIdRaw > 0 ? $selfCategoryIdRaw : null;

        $blogId = BID;
        $repository = $this->repository();

        // ブログコードとの衝突（自ブログにコードが未設定の場合のみ、ドメイン内で重複していないか）
        if (!ACMS_RAM::blogCode(BID)) {
            $domain = (string) ACMS_RAM::blogDomain(BID);
            if ($this->blogRepository()->existsByDomainAndCode($domain, $code)) {
                return false;
            }
        }

        // 同ブログ内の重複（strict mode の場合のみ同じ親内で重複チェック）
        $strictParent = config('category_order_strict_mode') === 'on';
        if ($repository->existsByCode($code, $blogId, $strictParent, $parentCategoryId, $excludeCategoryId)) {
            return false;
        }

        // 祖先ブログのグローバルカテゴリー
        if ($repository->existsGlobalCategoryInAncestorBlogs($code, $blogId)) {
            return false;
        }
        if ($scope === 'local') {
            return true;
        }

        // 自身が global の場合、子孫ブログのカテゴリーと衝突しないか
        return !$repository->existsCategoryInDescendantBlogs($code, $blogId);
    }
}
