<?php

namespace Modules\ProductCompat\Entities\Services;

use App\Product;
use Modules\ProductCompat\Entities\PcPartNumber;
use Modules\ProductCompat\Entities\PcProductBarcode;

/**
 * Gather the FULL data-bearing context of a product for enrichment / cleanup:
 * name, sku, brand, category, description and the configured custom fields
 * (CF1–6 by default), plus the flat list of identifiers currently attributed to
 * the product (structured part numbers/barcodes). The custom fields hold blobs
 * of many part numbers/serials — we pass both the RAW value and a safe coarse
 * token split (on , ; / newline — NEVER on space or dash, which live inside real
 * part numbers like PA3732U-1BRS / BT.00603.006) so the AI can extract & judge
 * each one without us corrupting identifiers.
 */
class ProductContext
{
    /** Which product columns are treated as data-bearing custom fields. */
    public static function fields(): array
    {
        return (array) config('productcompat.context_fields', [
            'product_custom_field1', 'product_custom_field2', 'product_custom_field3',
            'product_custom_field4', 'product_custom_field5', 'product_custom_field6',
        ]);
    }

    /**
     * @return array{
     *   name:string, sku:string, brand:string, category:string, sub_category:string,
     *   description:string, custom_fields:array<string,string>,
     *   existing_identifiers:string[], custom_field_tokens:string[]
     * }
     */
    public static function gather(Product $product): array
    {
        $custom = [];
        $tokens = [];
        foreach (self::fields() as $col) {
            $val = trim((string) ($product->{$col} ?? ''));
            if ($val === '') {
                continue;
            }
            $label = 'CF' . preg_replace('/\D+/', '', $col);
            $custom[$label] = mb_substr($val, 0, 1000);
            foreach (self::tokenize($val) as $t) {
                $tokens[$t] = true; // de-dupe across fields
            }
        }

        // Identifiers already attributed to the product (these need a relevance
        // check). Kept separate from the raw CF tokens the AI can add from.
        $existing = PcPartNumber::where('product_id', $product->id)->pluck('part_number')
            ->merge(PcProductBarcode::where('product_id', $product->id)->pluck('barcode'))
            ->map(fn ($v) => trim((string) $v))
            ->filter()
            ->unique()
            ->values()
            ->all();

        return [
            'name'         => (string) $product->name,
            'sku'          => (string) $product->sku,
            'brand'        => (string) optional($product->brand)->name,
            'category'     => (string) optional($product->category)->name,
            'sub_category' => (string) optional($product->sub_category)->name,
            'description'  => mb_substr(trim(strip_tags((string) $product->product_description)), 0, 600),
            'custom_fields'         => $custom,
            'custom_field_tokens'   => array_slice(array_keys($tokens), 0, 200),
            'existing_identifiers'  => $existing,
        ];
    }

    /**
     * Safe coarse split: commas, semicolons, slashes and newlines only. Spaces
     * and dashes are preserved because they occur inside real part numbers.
     */
    public static function tokenize(string $raw): array
    {
        $parts = preg_split('#[,;/\r\n|]+#', $raw) ?: [];
        $parts = array_map(fn ($p) => trim(preg_replace('/\s+/', ' ', $p)), $parts);

        return array_values(array_filter($parts, fn ($p) => $p !== '' && mb_strlen($p) <= 80));
    }
}
