<?php

namespace Acms\Services\ConfigSet;

use SQL;
use DB;

/**
 * コンフィグセットの永続化を担う実装。
 *
 * インポートは常に新規の config_set を作る（config_set には form_code のような自然キーが
 * 無いため、既存セットへの上書きは行わない。CMS-7831）。config_rule_id / config_module_id は
 * ルール・モジュール単位の紐付けであり、コンフィグセット単体のインポート先では対応する
 * ルール・モジュールが存在するとは限らないため、常に NULL で登録する。
 */
class Repository implements RepositoryInterface
{
    /**
     * @inheritDoc
     */
    public function findRawById(int $configSetId): ?array
    {
        if ($configSetId < 1) {
            return null;
        }
        $sql = SQL::newSelect('config_set');
        $sql->addWhereOpr('config_set_id', $configSetId);
        $row = DB::query($sql->get(dsn()), 'row');

        return is_array($row) ? $row : null;
    }

    /**
     * @inheritDoc
     */
    public function findConfigRows(int $configSetId): array
    {
        if ($configSetId < 1) {
            return [];
        }
        $sql = SQL::newSelect('config');
        $sql->addWhereOpr('config_set_id', $configSetId);
        $sql->setOrder('config_sort', 'ASC');
        $statement = DB::query($sql->get(dsn()), 'exec');

        $rows = [];
        while ($row = DB::next($statement)) {
            $rows[] = $row;
        }

        return $rows;
    }

    /**
     * @inheritDoc
     */
    public function insertConfigSet(int $blogId, array $attributes): int
    {
        $configSetId = (int) DB::query(SQL::nextval('config_set_id', dsn()), 'seq');

        $sql = SQL::newInsert('config_set');
        $sql->addInsert('config_set_id', $configSetId);
        $sql->addInsert('config_set_type', $attributes['config_set_type'] ?? null);
        $sql->addInsert('config_set_sort', $this->nextSort($blogId));
        $sql->addInsert('config_set_name', $attributes['config_set_name'] ?? '');
        $sql->addInsert('config_set_description', $attributes['config_set_description'] ?? '');
        $sql->addInsert('config_set_scope', $attributes['config_set_scope'] ?? 'local');
        $sql->addInsert('config_set_blog_id', $blogId);
        DB::query($sql->get(dsn()), 'exec');

        return $configSetId;
    }

    /**
     * @inheritDoc
     */
    public function insertConfig(int $configSetId, int $blogId, array $attributes): void
    {
        $sql = SQL::newInsert('config');
        $sql->addInsert('config_key', $attributes['config_key']);
        $sql->addInsert('config_value', $attributes['config_value']);
        $sql->addInsert('config_sort', $attributes['config_sort']);
        $sql->addInsert('config_rule_id', null);
        $sql->addInsert('config_module_id', null);
        $sql->addInsert('config_set_id', $configSetId);
        $sql->addInsert('config_blog_id', $blogId);
        DB::query($sql->get(dsn()), 'exec');
    }

    /**
     * @inheritDoc
     */
    public function optimizeSequence(): void
    {
        DB::query(SQL::optimizeSeq('config_set_id', dsn()), 'seq');
    }

    /**
     * @inheritDoc
     */
    public function updateConfigSetMeta(int $configSetId, array $attributes): void
    {
        $sql = SQL::newUpdate('config_set');
        $sql->addUpdate('config_set_name', $attributes['config_set_name'] ?? '');
        $sql->addUpdate('config_set_description', $attributes['config_set_description'] ?? '');
        $sql->addWhereOpr('config_set_id', $configSetId);
        DB::query($sql->get(dsn()), 'exec');
    }

    /**
     * @inheritDoc
     */
    public function deleteConfigRows(int $configSetId): void
    {
        $sql = SQL::newDelete('config');
        $sql->addWhereOpr('config_set_id', $configSetId);
        DB::query($sql->get(dsn()), 'exec');
    }

    /**
     * @inheritDoc
     */
    public function findCategoryCodeById(int $categoryId): ?string
    {
        if ($categoryId < 1) {
            return null;
        }
        $sql = SQL::newSelect('category');
        $sql->setSelect('category_code');
        $sql->addWhereOpr('category_id', $categoryId);
        $code = DB::query($sql->get(dsn()), 'one');

        return is_string($code) && $code !== '' ? $code : null;
    }

    /**
     * @inheritDoc
     */
    public function findCategoryIdByCodeInBlog(int $blogId, string $categoryCode): ?int
    {
        if ($categoryCode === '') {
            return null;
        }
        $sql = SQL::newSelect('category');
        $sql->setSelect('category_id');
        $sql->addWhereOpr('category_blog_id', $blogId);
        $sql->addWhereOpr('category_code', $categoryCode);
        $statement = DB::query($sql->get(dsn()), 'exec');

        $categoryIds = [];
        while ($row = DB::next($statement)) {
            $categoryIds[] = (int) $row['category_id'];
        }

        return count($categoryIds) === 1 ? $categoryIds[0] : null;
    }

    /**
     * 対象ブログの既存コンフィグセットの最大 sort の次番を返す
     */
    private function nextSort(int $blogId): int
    {
        $sql = SQL::newSelect('config_set');
        $sql->setSelect('config_set_sort');
        $sql->addWhereOpr('config_set_blog_id', $blogId);
        $sql->setOrder('config_set_sort', 'DESC');
        $sql->setLimit(1);

        return (int) DB::query($sql->get(dsn()), 'one') + 1;
    }
}
