<?php

declare(strict_types=1);

namespace App\Business\Product;

use App\Models\DberpProduct;
use App\Models\DberpProductSkuSpecValue;
use App\Models\DberpProductSpecGroup;
use App\Models\DberpProductSpecValue;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

/**
 * 商品规格组与规格取值的持久化（Filament 表单写入）。
 */
final class ProductSpecGroupWriter
{
    /**
     * @param  array{name: string, sort_order?: int|string, spec_value_rows?: list<array{id?: int|string|null, value_label?: string|null, sort_order?: int|string|null}>}  $data
     */
    public function createGroupWithValues(DberpProduct $product, array $data): DberpProductSpecGroup
    {
        return DB::transaction(function () use ($product, $data): DberpProductSpecGroup {
            $rows = $this->normalizeSpecValueRows($data['spec_value_rows'] ?? []);
            $this->assertSpecValueRowsNonEmpty($rows);

            $group = $product->specGroups()->create([
                'name' => trim((string) ($data['name'] ?? '')),
                'sort_order' => (int) ($data['sort_order'] ?? 0),
            ]);

            $this->insertSpecValues($group, $rows);

            return $group->fresh();
        });
    }

    /**
     * @param  array{name?: string, sort_order?: int|string, spec_value_rows?: list<array{id?: int|string|null, value_label?: string|null, sort_order?: int|string|null}>}  $data
     */
    public function updateGroupWithValues(DberpProductSpecGroup $group, array $data): void
    {
        DB::transaction(function () use ($group, $data): void {
            if (array_key_exists('name', $data)) {
                $group->name = trim((string) $data['name']);
            }
            if (array_key_exists('sort_order', $data)) {
                $group->sort_order = (int) $data['sort_order'];
            }
            $group->save();

            if (! array_key_exists('spec_value_rows', $data)) {
                return;
            }

            $rows = $this->normalizeSpecValueRows($data['spec_value_rows']);
            $this->assertSpecValueRowsNonEmpty($rows);
            $this->syncSpecValues($group, $rows);
        });
    }

    /**
     * @param  list<array{id?: int|string|null, value_label: string, sort_order: int}>  $rows
     */
    private function insertSpecValues(DberpProductSpecGroup $group, array $rows): void
    {
        foreach ($rows as $row) {
            $group->specValues()->create([
                'value_label' => $row['value_label'],
                'sort_order' => $row['sort_order'],
            ]);
        }
    }

    /**
     * @param  list<array{id?: int|string|null, value_label: string, sort_order: int}>  $rows
     */
    private function syncSpecValues(DberpProductSpecGroup $group, array $rows): void
    {
        $incomingIds = collect($rows)->pluck('id')->filter()->map(fn ($id): int => (int) $id)->all();

        $existing = $group->specValues()->orderBy('sort_order')->orderBy('id')->get();
        foreach ($existing as $value) {
            if (in_array($value->getKey(), $incomingIds, true)) {
                continue;
            }

            if (DberpProductSkuSpecValue::query()->where('product_spec_value_id', $value->getKey())->exists()) {
                throw ValidationException::withMessages([
                    'spec_value_rows' => (string) __('dberp.errors.product_spec_value_in_use'),
                ]);
            }

            $value->delete();
        }

        foreach ($rows as $row) {
            if ($row['id'] !== null) {
                $model = DberpProductSpecValue::query()
                    ->where('product_spec_group_id', $group->getKey())
                    ->whereKey($row['id'])
                    ->first();
                if ($model !== null) {
                    $model->update([
                        'value_label' => $row['value_label'],
                        'sort_order' => $row['sort_order'],
                    ]);

                    continue;
                }
            }

            $group->specValues()->create([
                'value_label' => $row['value_label'],
                'sort_order' => $row['sort_order'],
            ]);
        }
    }

    /**
     * @param  list<array<string, mixed>>  $raw
     * @return list<array{id: int|null, value_label: string, sort_order: int}>
     */
    private function normalizeSpecValueRows(array $raw): array
    {
        $out = [];
        foreach ($raw as $row) {
            $label = trim((string) ($row['value_label'] ?? ''));
            if ($label === '') {
                continue;
            }
            $idRaw = $row['id'] ?? null;
            $out[] = [
                'id' => $idRaw === null || $idRaw === '' ? null : (int) $idRaw,
                'value_label' => $label,
                'sort_order' => (int) ($row['sort_order'] ?? 0),
            ];
        }

        return $out;
    }

    /**
     * @param  list<array{id: int|null, value_label: string, sort_order: int}>  $rows
     */
    private function assertSpecValueRowsNonEmpty(array $rows): void
    {
        if ($rows === []) {
            throw ValidationException::withMessages([
                'spec_value_rows' => (string) __('dberp.errors.product_spec_values_required'),
            ]);
        }
    }
}
