<?php

namespace App\Support;

use DOMElement;

class CatalogProductLinkMatcher
{
    public static function isProductCta(?DOMElement $link): bool
    {
        if ($link === null || ! $link->hasAttribute('href')) {
            return false;
        }

        $href = trim($link->getAttribute('href'));

        if ($href === '' || ! preg_match('#^https?://#i', $href)) {
            return false;
        }

        if (self::isHeaderBrandLink($link)) {
            return false;
        }

        if (self::matchesClassPattern($link, 'cta')
            || self::matchesClassPattern($link, 'row-cta')
            || self::hasClass($link, 'row-overlay-link')
            || self::hasClass($link, 'row-thumb-link')) {
            return true;
        }

        return self::closest($link, '.product-row, .product-card, .product-rows, #products, section[id*="product"], article') !== null;
    }

    private static function isHeaderBrandLink(DOMElement $link): bool
    {
        return self::closest($link, 'header.topbar a.brand, header .logo, .topbar .brand') !== null;
    }

    private static function closest(DOMElement $element, string $selectors): ?DOMElement
    {
        foreach (array_map(trim(...), explode(',', $selectors)) as $selector) {
            if ($selector === '') {
                continue;
            }

            $match = self::walkAncestors($element, $selector);

            if ($match instanceof DOMElement) {
                return $match;
            }
        }

        return null;
    }

    private static function walkAncestors(DOMElement $element, string $selector): ?DOMElement
    {
        $current = $element;

        while ($current instanceof DOMElement) {
            if (self::matchesSelector($current, $selector)) {
                return $current;
            }

            $parent = $current->parentNode;

            if (! $parent instanceof DOMElement) {
                return null;
            }

            $current = $parent;
        }

        return null;
    }

    private static function matchesSelector(DOMElement $element, string $selector): bool
    {
        if (str_starts_with($selector, '#')) {
            return $element->getAttribute('id') === substr($selector, 1);
        }

        if (str_starts_with($selector, '.')) {
            return self::hasClass($element, substr($selector, 1));
        }

        if (str_contains($selector, '[id*="')) {
            preg_match('/^([a-z0-9-]+)\[id\*="([^"]+)"\]$/i', $selector, $matches);

            if (($matches[1] ?? '') !== '' && ($matches[2] ?? '') !== '') {
                $tag = strtolower($matches[1]);
                $needle = $matches[2];

                return strtolower($element->tagName) === $tag
                    && str_contains($element->getAttribute('id'), $needle);
            }
        }

        if (str_contains($selector, ' ')) {
            return false;
        }

        return strtolower($element->tagName) === strtolower($selector);
    }

    private static function hasClass(DOMElement $element, string $className): bool
    {
        $classes = preg_split('/\s+/', trim($element->getAttribute('class'))) ?: [];

        return in_array($className, $classes, true);
    }

    private static function matchesClassPattern(DOMElement $element, string $needle): bool
    {
        $classes = preg_split('/\s+/', trim($element->getAttribute('class'))) ?: [];

        foreach ($classes as $className) {
            if ($className !== '' && str_contains($className, $needle)) {
                return true;
            }
        }

        return false;
    }
}
