<?php

declare(strict_types=1);

namespace App\Commerce\Dto;

final readonly class CommerceReferenceContext
{
    /**
     * @param string[] $focusTerms
     */
    public function __construct(
        public string $productName,
        public ?string $productNumber = null,
        public ?string $manufacturer = null,
        public ?string $url = null,
        public string $sourceType = 'conversation',
        public float $confidence = 0.0,
        public ?string $resolvedFromPrompt = null,
        public ?string $resolvedAt = null,
        public array $focusTerms = [],
    ) {
    }

    /**
     * @param array<string, mixed> $payload
     */
    public static function fromArray(array $payload): ?self
    {
        $productName = self::normalizeNullableString($payload['productName'] ?? null);

        if ($productName === null) {
            return null;
        }

        return new self(
            productName: $productName,
            productNumber: self::normalizeNullableString($payload['productNumber'] ?? null),
            manufacturer: self::normalizeNullableString($payload['manufacturer'] ?? null),
            url: self::normalizeNullableString($payload['url'] ?? null),
            sourceType: self::normalizeNullableString($payload['sourceType'] ?? null) ?? 'conversation',
            confidence: isset($payload['confidence']) && is_numeric($payload['confidence']) ? (float) $payload['confidence'] : 0.0,
            resolvedFromPrompt: self::normalizeNullableString($payload['resolvedFromPrompt'] ?? null),
            resolvedAt: self::normalizeNullableString($payload['resolvedAt'] ?? null),
            focusTerms: self::normalizeStringList($payload['focusTerms'] ?? []),
        );
    }

    /**
     * @return array<string, mixed>
     */
    public function toArray(): array
    {
        return [
            'productName' => $this->productName,
            'productNumber' => $this->productNumber,
            'manufacturer' => $this->manufacturer,
            'url' => $this->url,
            'sourceType' => $this->sourceType,
            'confidence' => $this->confidence,
            'resolvedFromPrompt' => $this->resolvedFromPrompt,
            'resolvedAt' => $this->resolvedAt,
            'focusTerms' => $this->focusTerms,
        ];
    }

    public function hasStrongIdentity(): bool
    {
        return $this->productNumber !== null || $this->confidence >= 0.8;
    }

    public function buildReferenceSearchText(): string
    {
        $parts = [];

        if ($this->productName !== '') {
            $parts[] = $this->productName;
        }

        if (
            $this->productNumber !== null
            && $this->productNumber !== ''
            && stripos($this->productName, $this->productNumber) === false
        ) {
            $parts[] = $this->productNumber;
        }

        foreach ($this->focusTerms as $focusTerm) {
            if ($focusTerm === '') {
                continue;
            }

            $alreadyIncluded = false;

            foreach ($parts as $part) {
                if (stripos($part, $focusTerm) !== false) {
                    $alreadyIncluded = true;
                    break;
                }
            }

            if (!$alreadyIncluded) {
                $parts[] = $focusTerm;
            }
        }

        return trim(implode(' ', $parts));
    }

    private static function normalizeNullableString(mixed $value): ?string
    {
        if (!is_string($value)) {
            return null;
        }

        $value = trim($value);

        return $value !== '' ? $value : null;
    }

    /**
     * @param mixed $value
     * @return string[]
     */
    private static function normalizeStringList(mixed $value): array
    {
        if (!is_array($value)) {
            return [];
        }

        $out = [];

        foreach ($value as $item) {
            if (!is_string($item)) {
                continue;
            }

            $item = trim($item);

            if ($item === '') {
                continue;
            }

            $out[] = $item;
        }

        return array_values(array_unique($out));
    }
}