<?php

namespace Acms\Services\Blog;

use SQL;
use DB;

/**
 * 単一 bin → 単一 blog への yaml 投入を実行する。
 *
 * 用途: ACMS_POST_Backup_BlogImport 等、単発でブログをインポートする呼び出し。
 *       (複数 bin の同時投入は {@see MultiBlogImport} を使う)
 *
 * 共通処理は `ImportTrait` に集約。本クラスの責務は:
 *  - 入力 yaml 1 本から ID マップを採番する `prepareIds()` の実装
 *  - 公開メソッド `run($bid, $yaml)` の薄いラッパー
 */
class Import
{
    use ImportTrait;

    /**
     * @var array
     */
    protected $yaml;

    /**
     * @var int
     */
    protected $blogId;

    /**
     * @var int
     */
    protected $userId;

    /**
     * @var array
     */
    protected $ids;

    /**
     * @var array
     */
    protected $errors;

    /**
     * @var array
     */
    protected $mediaFieldFix = [];

    /**
     * @param string $yaml 投入する yaml 文字列
     * @param int $blogId 投入対象 blog_id
     * @param int $userId entry_last_update_user_id 等で使う実行者 user_id
     * @return array エラーメッセージの一覧
     */
    public function run(string $yaml, int $blogId, int $userId): array
    {
        // 投入先 blog の既存データを破壊リプレース
        $this->blogId = $blogId;
        $this->dropData();

        // yaml 内の <table>_blog_id を投入先 blog_id で書き換え
        $normalizedYaml = $this->rewriteBlogIdInYaml($yaml, $blogId);

        return $this->executeImport($normalizedYaml, $blogId, $userId);
    }

    /**
     * 単発 import では「入力 yaml 1 本のみ」から ID を採番する。
     */
    protected function prepareIds(): void
    {
        $this->ids = [];
        $tables = [
            'column', 'alias', 'form',
            'entry', 'fulltext', 'media',
            'module', 'rule', 'config_set',
        ];
        foreach ($tables as $table) {
            $this->registerNewIDsForTable($table);
        }
        $this->registerCategoryNewId();
    }

    private function registerNewIDsForTable(string $table): void
    {
        if (!$this->existsYaml($table)) {
            return;
        }
        foreach ($this->yaml[$table] as $record) {
            if (!is_array($record) || !isset($record[$table . '_id'])) {
                continue;
            }
            $id = $record[$table . '_id'];
            if (isset($this->ids[$table][$id])) {
                continue;
            }
            if ($table === 'column') {
                $this->ids[$table][$id] = uuidv4();
            } else {
                $this->ids[$table][$id] = DB::query(SQL::nextval($table . '_id', dsn()), 'seq');
            }
        }
    }

    /**
     * カテゴリーIDの新規マッピング。同名コードが既存ブログに存在する場合は再利用、
     * 無い場合のみ nextval で新規採番する (旧 setup/lib/import.php と同じセマンティクス)。
     *
     * 親IDでソートしてから処理することで、子の親参照が必ず解決済みになる。
     */
    private function registerCategoryNewId(): void
    {
        if (!$this->existsYaml('category')) {
            return;
        }

        // ステップ1: YAMLデータからカテゴリーコードを収集
        $codeArray = [];
        foreach ($this->yaml['category'] as $record) {
            $codeArray[] = $record['category_code'];
        }
        $codeArray = array_unique($codeArray);

        // ステップ2: 既存のカテゴリーをDBから検索
        $sql = SQL::newSelect('category');
        $sql->addWhereOpr('category_blog_id', $this->blogId);
        $sql->addWhereIn('category_code', $codeArray);
        $q = $sql->get(dsn());
        $statement = DB::query($q, 'exec');

        // ステップ3: 既存カテゴリーを「コード:親ID」の組でテーブル化
        $categoryTable = [];
        while ($row = DB::next($statement)) {
            $code = $row['category_code'];
            $parent = intval($row['category_parent']);
            $key = $code . ':' . $parent;
            $categoryTable[$key] = $row['category_id'];
        }

        // ステップ4: YAMLのカテゴリーを親ID順にソート
        $categories = $this->yaml['category'];
        usort($categories, function ($a, $b) {
            $parentA = intval($a['category_parent']);
            $parentB = intval($b['category_parent']);
            return $parentA <=> $parentB;
        });

        // ステップ5: 各カテゴリーのIDマッピング
        foreach ($categories as $record) {
            $id = $record['category_id'];
            $code = $record['category_code'];
            $oldParentId = intval($record['category_parent']);

            $newParentId = 0;
            if ($oldParentId > 0 && isset($this->ids['category'][$oldParentId])) {
                $newParentId = $this->ids['category'][$oldParentId];
            }

            $key = $code . ':' . $newParentId;

            if (isset($categoryTable[$key])) {
                $this->ids['category'][$id] = $categoryTable[$key];
            } else {
                $this->ids['category'][$id] = DB::query(SQL::nextval('category_id', dsn()), 'seq');
            }
        }
    }
}
