<?php

declare(strict_types=1);

/*
 * This file is part of the VITICORE2 package.
 *
 * (c) CP Creation <web@cpcreation.fr>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace CpCreation\VitiCore\Commerce\Catalog;

use CpCreation\VitiCore\Bridge\Symfony\Bundle\Provider\LocaleProvider;
use CpCreation\VitiCore\Commerce\Catalog\Api\Resource\CatalogSeller;
use CpCreation\VitiCore\Commerce\Catalog\Api\Resource\CatalogSellerAddress;
use CpCreation\VitiCore\Commerce\Catalog\Api\Resource\CatalogSellerLink;
use CpCreation\VitiCore\Contact\ContactProvider;
use CpCreation\VitiCore\Contact\Model\Contact;

final readonly class CatalogSellerProjector
{
    public function __construct(
        private CatalogIdentity $identity,
        private ContactProvider $contactProvider,
        private LocaleProvider $localeProvider,
        private CatalogStorefrontUrlGenerator $urlGenerator,
    ) {}

    public function project(): CatalogSeller
    {
        $contact = $this->contactProvider->getContact();
        $locales = $this->locales();
        $shopUrls = $this->localizedUrls('shop', $locales);

        return new CatalogSeller(
            id: $this->identity->sellerId(),
            source_contact_id: $contact?->getId()->toString() ?? '',
            display_name: $this->displayName($contact),
            legal_name: $this->nullableText($contact?->getEditorCompany())
                ?? $this->nullableText($contact?->getCompany()),
            store_country: mb_strtoupper($this->nullableText($contact?->getCountry()) ?? ''),
            address: new CatalogSellerAddress(
                street: $this->nullableText($contact?->getAddress()),
                postal_code: $this->nullableText($contact?->getPostal()),
                city: $this->nullableText($contact?->getCity()),
                country: mb_strtoupper($this->nullableText($contact?->getCountry()) ?? ''),
            ),
            website_url: $this->websiteUrl($shopUrls),
            links: $this->links($locales),
        );
    }

    /**
     * @param list<string> $locales
     *
     * @return list<CatalogSellerLink>
     */
    private function links(array $locales): array
    {
        $links = [];

        foreach ([
            'privacy_policy' => 'private-policy',
            'terms_of_service' => 'cgv',
            'refund_policy' => 'shop_withdrawal',
        ] as $type => $route) {
            $urls = $this->localizedUrls($route, $locales);
            if ([] !== $urls) {
                $links[] = new CatalogSellerLink($type, $urls);
            }
        }

        return $links;
    }

    /**
     * @param list<string> $locales
     *
     * @return array<string, string>
     */
    private function localizedUrls(string $route, array $locales): array
    {
        $urls = [];

        foreach ($locales as $locale) {
            $url = $this->urlGenerator->route($route, $locale);
            if (null !== $url) {
                $urls[$locale] = $url;
            }
        }

        return $urls;
    }

    /**
     * @param array<string, string> $shopUrls
     */
    private function websiteUrl(array $shopUrls): ?string
    {
        foreach ($shopUrls as $shopUrl) {
            $origin = $this->urlGenerator->origin($shopUrl);
            if (null !== $origin) {
                return $origin;
            }
        }

        return null;
    }

    private function displayName(?Contact $contact): string
    {
        $company = $this->nullableText($contact?->getCompany())
            ?? $this->nullableText($contact?->getEditorCompany());
        if (null !== $company) {
            return $company;
        }

        return mb_trim(implode(' ', array_filter([
            $this->nullableText($contact?->getFirstname()),
            $this->nullableText($contact?->getLastname()),
        ])));
    }

    /**
     * @return list<string>
     */
    private function locales(): array
    {
        $defaultLocale = mb_trim($this->localeProvider->getDefaultLocale());
        $locales = [];
        foreach ($this->localeProvider->getLocales() as $locale) {
            if (!\is_string($locale)) {
                continue;
            }

            $locale = mb_trim($locale);
            if ('' !== $locale && $locale !== $defaultLocale) {
                $locales[$locale] = true;
            }
        }
        $locales = array_keys($locales);
        sort($locales);

        return '' !== $defaultLocale ? [$defaultLocale, ...$locales] : $locales;
    }

    private function nullableText(?string $value): ?string
    {
        if (null === $value) {
            return null;
        }

        $value = mb_trim($value);

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