<?php

declare(strict_types=1);

namespace App\Business\Product;

use App\Models\DberpProduct;
use App\Models\DberpProductSku;
use App\Models\DberpProductSkuSpecValue;
use App\Models\DberpProductSpecValue;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;

/**
 * 根据规格维度笛卡尔积生成缺失的规格组合行，不修改、不删除已有记录。
 */
final class ProductSkuCombinationGenerator
{
    /**
     * @return int 新建规格组合数量
     */
    public function generateMissing(DberpProduct $product): int
    {
        if (! $product->spec_enabled) {
            return 0;
        }

        return DB::transaction(function () use ($product): int {
            $groups = $product->specGroups()
                ->with(['specValues' => fn ($q) => $q->orderBy('sort_order')->orderBy('id')])
                ->orderBy('sort_order')
                ->orderBy('id')
                ->get();

            if ($groups->isEmpty() || $groups->contains(fn ($g): bool => $g->specValues->isEmpty())) {
                return 0;
            }

            /** @var list<Collection<int, DberpProductSpecValue>> $valueLists */
            $valueLists = $groups->map(fn ($g) => $g->specValues)->all();
            $tuples = $this->cartesian($valueLists);
            $created = 0;
            $maxSort = (int) DberpProductSku::query()->where('product_id', $product->getKey())->max('sort_order');

            foreach ($tuples as $tuple) {
                $valueIds = collect($tuple)->pluck('id')->sort()->values()->all();
                if ($this->findSkuWithExactSpecValueIds($product, $valueIds) !== null) {
                    continue;
                }

                $sortedTuple = collect($tuple)->sortBy(fn (DberpProductSpecValue $v): int => (int) $v->product_spec_group_id)->values()->all();
                $specText = collect($sortedTuple)->pluck('value_label')->implode(' / ');

                $sku = DberpProductSku::query()->create([
                    'product_id' => $product->getKey(),
                    'sku_code' => '',
                    'barcode' => null,
                    'spec_text' => $specText,
                    'cost_price' => $product->cost_price,
                    'sales_price' => $product->sales_price,
                    'unit_id' => null,
                    'is_enabled' => true,
                    'is_default' => false,
                    'sort_order' => ++$maxSort,
                    'remark' => null,
                ]);

                foreach ($sortedTuple as $value) {
                    DberpProductSkuSpecValue::query()->create([
                        'product_sku_id' => $sku->getKey(),
                        'product_spec_group_id' => (int) $value->product_spec_group_id,
                        'product_spec_value_id' => $value->getKey(),
                    ]);
                }

                $created++;
            }

            $this->ensureDefaultWhenMissing($product);

            return $created;
        });
    }

    /**
     * @param  list<int|string>  $valueIds  规格取值 id 集合（顺序无关）
     */
    public function findSkuWithExactSpecValueIds(DberpProduct $product, array $valueIds, ?int $ignoreSkuId = null): ?DberpProductSku
    {
        $target = collect($valueIds)->map(fn ($id): int => (int) $id)->sort()->values()->implode(',');
        if ($target === '') {
            return null;
        }

        $skus = DberpProductSku::query()
            ->where('product_id', $product->getKey())
            ->with(['skuSpecAssignments'])
            ->get();

        foreach ($skus as $sku) {
            if ($ignoreSkuId !== null && (int) $sku->getKey() === $ignoreSkuId) {
                continue;
            }

            $ids = $sku->skuSpecAssignments
                ->pluck('product_spec_value_id')
                ->map(fn ($id): int => (int) $id)
                ->sort()
                ->values()
                ->implode(',');
            if ($ids === $target && $ids !== '') {
                return $sku;
            }
        }

        return null;
    }

    private function ensureDefaultWhenMissing(DberpProduct $product): void
    {
        $hasDefault = DberpProductSku::query()
            ->where('product_id', $product->getKey())
            ->where('is_default', true)
            ->exists();

        if ($hasDefault) {
            return;
        }

        $first = DberpProductSku::query()
            ->where('product_id', $product->getKey())
            ->orderBy('sort_order')
            ->orderBy('id')
            ->first();

        if ($first !== null) {
            $first->forceFill(['is_default' => true])->save();
        }
    }

    /**
     * @param  list<Collection<int, DberpProductSpecValue>>  $lists
     * @return list<list<DberpProductSpecValue>>
     */
    private function cartesian(array $lists): array
    {
        if ($lists === []) {
            return [];
        }

        /** @var list<list<DberpProductSpecValue>> $acc */
        $acc = [[]];

        foreach ($lists as $list) {
            $next = [];
            foreach ($acc as $prefix) {
                foreach ($list as $item) {
                    $next[] = [...$prefix, $item];
                }
            }
            $acc = $next;
        }

        return $acc;
    }
}
