<?php
/**
 * Copyright since 2025 InPost S.A.
 *
 * For the full license information, please view the LICENSE file bundled with the module.
 *
 * @author InPost S.A.
 * @copyright since 2025 InPost S.A.
 * @license MIT
 */

declare(strict_types=1);

namespace InPost\International\Shipment\Order;

use InPost\International\Common\DTO\Address;
use InPost\International\Common\DTO\ContactDetails;
use InPost\International\Country;
use InPost\International\Delivery\Util\PhoneNumberResolver;
use InPost\International\PrestaShop\ObjectModel\ObjectManagerInterface;
use InPost\International\Shipment\Message\CreateShipmentCommand;
use PrestaShop\PrestaShop\Core\Domain\Address\Exception\AddressNotFoundException;
use PrestaShop\PrestaShop\Core\Domain\Customer\Exception\CustomerNotFoundException;

if (!defined('_PS_VERSION_')) {
    exit;
}

final class RecipientDetailsBuilder implements CreateShipmentCommandBuilderInterface
{
    /**
     * @var ObjectManagerInterface
     */
    private $manager;

    /**
     * @var PhoneNumberResolver
     */
    private $phoneNumberResolver;

    public function __construct(ObjectManagerInterface $manager, PhoneNumberResolver $phoneNumberResolver)
    {
        $this->manager = $manager;
        $this->phoneNumberResolver = $phoneNumberResolver;
    }

    public function buildCommand(CreateShipmentCommand $command, \Order $order): void
    {
        $customer = $this->manager->getRepository(\Customer::class)->find((int) $order->id_customer);

        if (null === $customer) {
            throw new CustomerNotFoundException(sprintf('Customer with id "%s" was not found.', $order->id_customer));
        }

        $address = $this->manager->getRepository(\Address::class)->find((int) $order->id_address_delivery);

        if (null === $address) {
            throw new AddressNotFoundException(sprintf('Address with id "%s" was not found.', $order->id_address_delivery));
        }

        $command->setRecipient($this->mapCustomerDetails($customer, $address));
        $command->setDestinationAddress($this->mapAddressData($address));
    }

    private function mapCustomerDetails(\Customer $customer, \Address $address): ContactDetails
    {
        $phone = $this->phoneNumberResolver->resolve($address);

        return (new ContactDetails())
            ->setCompanyName($address->company)
            ->setFirstName($address->firstname)
            ->setLastName($address->lastname)
            ->setEmail($customer->email)
            ->setPhone($phone);
    }

    private function mapAddressData(\Address $address): Address
    {
        $country = $this->getCountry($address);
        $streetAddress = $this->resolveStreetAddress($address, $country);

        return (new Address())
            ->setStreet($streetAddress['street'])
            ->setHouseNumber($streetAddress['house_number'])
            ->setFlatNumber($streetAddress['flat_number'] ?? null)
            ->setPostalCode($address->postcode)
            ->setCity($address->city)
            ->setCountryCode($country);
    }

    private function getCountry(\Address $address): ?Country
    {
        $country = $this->manager->getRepository(\Country::class)->find((int) $address->id_country);

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

        return Country::tryFrom((string) $country->iso_code);
    }

    /**
     * @return array{street: string, house_number: ?string, flat_number?: ?string}
     */
    private function resolveStreetAddress(\Address $address, ?Country $country): array
    {
        if (null !== $country) {
            $fullAddress = trim(sprintf('%s %s', $address->address1, $address->address2));

            if (preg_match($country->getAddressPattern(), $fullAddress, $matches)) {
                return $matches;
            }
        }

        return [
            'street' => $address->address1,
            'house_number' => $address->address2,
        ];
    }
}
