<?php

namespace App\Services\Scraping;

use App\Services\ProductPageScraper;
use Illuminate\Support\Facades\Log;

class CatalogListingProductValidator
{
    public function __construct(private ProductPageScraper $scraper) {}

    /**
     * Ensure each candidate has a product-specific image and a normalized price.
     *
     * @param  list<array<string, mixed>>  $candidates
     * @return list<array<string, mixed>>
     */
    public function validateAndEnrich(array $candidates, bool $trustProvidedImages = false): array
    {
        $validated = [];
        $seenImages = [];

        foreach ($candidates as $candidate) {
            if (! is_array($candidate)) {
                continue;
            }

            $url = trim((string) ($candidate['url'] ?? ''));

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

            $enriched = $this->enrichCandidate($candidate, $url, $trustProvidedImages);

            if (! $this->isComplete($enriched, $trustProvidedImages)) {
                Log::info('Catalog listing candidate dropped after validation', [
                    'url' => $url,
                    'price' => $enriched['price'] ?? null,
                    'image_url' => $enriched['image_url'] ?? null,
                ]);

                continue;
            }

            $imageUrl = (string) $enriched['image_url'];

            if (isset($seenImages[$imageUrl])) {
                Log::info('Catalog listing candidate dropped because image was reused', [
                    'url' => $url,
                    'image_url' => $imageUrl,
                ]);

                continue;
            }

            $seenImages[$imageUrl] = true;
            $validated[] = $enriched;
        }

        return $validated;
    }

    /**
     * @param  array<string, mixed>  $candidate
     * @return array<string, mixed>
     */
    private function enrichCandidate(array $candidate, string $url, bool $trustProvidedImages): array
    {
        $imageUrl = trim((string) ($candidate['image_url'] ?? ''));
        $price = trim((string) ($candidate['price'] ?? ''));
        $rating = trim((string) ($candidate['rating'] ?? ''));
        $reviewCount = trim((string) ($candidate['review_count'] ?? ''));
        $imageIsAcceptable = $this->imageIsAcceptable($candidate, $url, $trustProvidedImages);

        $needsProductPage = $price === ''
            || $imageUrl === ''
            || ! $imageIsAcceptable
            || $rating === ''
            || $reviewCount === '';

        if (! $needsProductPage) {
            return $this->normalizePrices($candidate);
        }

        $html = $this->scraper->fetchLiveHtml($url);

        if ($html === null) {
            $html = $this->scraper->fetchHtml($url);
        }

        if ($html === '') {
            return $this->normalizePrices($candidate);
        }

        if ($imageUrl === '' || ! $this->scraper->listingImageMatchesProduct($imageUrl, $url)) {
            $resolvedImage = $this->scraper->primaryImageUrl($html, $url);

            if (filled($resolvedImage) && $this->scraper->listingImageMatchesProduct($resolvedImage, $url)) {
                $candidate['image_url'] = $resolvedImage;
            } elseif ($imageUrl === '') {
                $candidate['image_url'] = null;
            }
        }

        if ($price === '') {
            $offer = $this->scraper->productOfferFromHtml($html);

            if (is_array($offer)) {
                $candidate['price'] = $offer['price'];
                $candidate['list_price'] = $offer['list_price'] ?? ($candidate['list_price'] ?? null);
            }
        }

        if ($rating === '' || $reviewCount === '') {
            $ratings = $this->scraper->productRatingsFromHtml($html);

            if (is_array($ratings)) {
                if ($rating === '' && filled($ratings['rating'] ?? null)) {
                    $candidate['rating'] = $ratings['rating'];
                }

                if ($reviewCount === '' && filled($ratings['review_count'] ?? null)) {
                    $candidate['review_count'] = $ratings['review_count'];
                }
            }
        }

        return $this->normalizePrices($candidate);
    }

    /**
     * @param  array<string, mixed>  $candidate
     * @return array<string, mixed>
     */
    private function normalizePrices(array $candidate): array
    {
        $price = trim((string) ($candidate['price'] ?? ''));
        $listPrice = trim((string) ($candidate['list_price'] ?? ''));

        if ($price !== '' && preg_match('/\$\s?([\d,]+(?:\.\d{2})?)/', $price, $match) === 1) {
            $candidate['price'] = '$'.str_replace(',', '', $match[1]);
        }

        if ($listPrice !== '' && preg_match('/\$\s?([\d,]+(?:\.\d{2})?)/', $listPrice, $match) === 1) {
            $candidate['list_price'] = '$'.str_replace(',', '', $match[1]);
        }

        if (($candidate['list_price'] ?? null) === ($candidate['price'] ?? null)) {
            $candidate['list_price'] = null;
        }

        return $candidate;
    }

    /**
     * @param  array<string, mixed>  $candidate
     */
    private function isComplete(array $candidate, bool $trustProvidedImages): bool
    {
        $url = trim((string) ($candidate['url'] ?? ''));
        $price = trim((string) ($candidate['price'] ?? ''));

        return $url !== ''
            && $price !== ''
            && $this->imageIsAcceptable($candidate, $url, $trustProvidedImages);
    }

    /**
     * @param  array<string, mixed>  $candidate
     */
    private function imageIsAcceptable(array $candidate, string $url, bool $trustProvidedImages): bool
    {
        $imageUrl = trim((string) ($candidate['image_url'] ?? ''));

        if ($imageUrl === '') {
            return false;
        }

        if ($trustProvidedImages || ($candidate['discovery_source'] ?? '') === 'json') {
            return true;
        }

        return $this->scraper->listingImageMatchesProduct($imageUrl, $url);
    }
}
