<?php

declare(strict_types=1);

namespace CpCreation\VitiCore\Commerce\Catalog;

use CpCreation\VitiCore\Commerce\Catalog\Api\Resource\CatalogProductStatus;
use CpCreation\VitiCore\Commerce\Catalog\Api\Resource\CatalogPublishability;
use CpCreation\VitiCore\Commerce\Catalog\Api\Resource\CatalogPublishabilityReason;
use CpCreation\VitiCore\Commerce\Catalog\Api\Resource\CatalogValidation;
use CpCreation\VitiCore\Product\Model\Product;
use CpCreation\VitiCore\Product\Model\ProductCategory;
use CpCreation\VitiCore\Product\Model\ProductPriceGroup;

final readonly class CatalogPublishabilityResolver
{
    public function resolve(
        Product $product,
        ?ProductCategory $category,
        CatalogProductStatus $status,
        CatalogValidation $validation,
    ): CatalogPublishability {
        $reasons = [];

        if ($category instanceof ProductCategory && !$category->isCommerceExportEnabled()) {
            $this->addReason($reasons, 'category_export_disabled', 'compliance.catalog_export_enabled');
        }

        if ($category instanceof ProductCategory && $category->isPageShop() && !$this->hasDefaultPriceGroup($product)) {
            $this->addReason($reasons, 'price_group_restricted', null);
        } elseif (!$status->published) {
            $this->addReason($reasons, 'storefront_not_public', 'status.published');
        }

        if (!$status->price_visible) {
            $this->addReason($reasons, 'price_hidden', 'status.price_visible');
        }

        $hasValidationError = false;
        foreach ($validation->issues as $issue) {
            if ('error' === $issue->severity) {
                $hasValidationError = true;
                $this->addReason($reasons, $issue->code, $issue->path);
            }
        }
        if (!$validation->valid && !$hasValidationError) {
            $this->addReason($reasons, 'catalog_validation_failed', 'validation.valid');
        }

        return new CatalogPublishability(
            status: [] === $reasons ? CatalogPublishability::PUBLISHABLE : CatalogPublishability::NOT_PUBLISHABLE,
            reasons: array_values($reasons),
        );
    }

    /**
     * @param array<string, CatalogPublishabilityReason> $reasons
     */
    private function addReason(array &$reasons, string $code, ?string $path): void
    {
        $reasons[$code . "\0" . $path] = new CatalogPublishabilityReason($code, $path);
    }

    private function hasDefaultPriceGroup(Product $product): bool
    {
        foreach ($product->getGroups() as $group) {
            if ($group instanceof ProductPriceGroup && $group->isByDefault() && !$group->isDeleted()) {
                return true;
            }
        }

        return false;
    }
}
