<?php

declare(strict_types=1);

namespace Gls\GlsPoland\Form\Type;

use Gls\GlsPoland\AdePlus\SenderAddress\ClientInterface;
use Gls\GlsPoland\AdePlus\SenderAddress\SenderAddress;
use Gls\GlsPoland\AdePlus\SenderAddress\SenderAddressesCollection;
use Gls\GlsPoland\AdePlus\SenderAddress\SenderAddressStatus;
use Gls\GlsPoland\Consignment\DTO\AddressDetails;
use Gls\GlsPoland\Exception\UnableToSelectSenderAddressException;
use Gls\GlsPoland\Form\DataTransformer\AddressDetailsToSenderAddressTransformer;
use Gls\GlsPoland\Translation\TranslatorAwareTrait;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\ChoiceList\Loader\CallbackChoiceLoader;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraint;

final class SenderDetailsType extends AbstractType
{
    use TranslatorAwareTrait;

    private $client;

    private $status;
    private $addresses;

    public function __construct(ClientInterface $client)
    {
        $this->client = $client;
    }

    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        if (SenderAddressStatus::FROM_DICTIONARY === $this->getStatus()) {
            $builder->addModelTransformer(new AddressDetailsToSenderAddressTransformer($this->getAddresses()));
        }
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        if (SenderAddressStatus::ANY === $status = $this->getStatus()) {
            $resolver->setDefaults([
                'data_class' => AddressDetails::class,
                'validation_groups' => [
                    Constraint::DEFAULT_GROUP,
                    AddressDetails::SENDER_ADDRESS_VALIDATION_GROUP,
                    AddressDetails::CONTACT_INFO_VALIDATION_GROUP,
                ],
            ]);
        } elseif (SenderAddressStatus::FROM_DICTIONARY === $status) {
            $resolver->setDefaults([
                'data_class' => null,
                'required' => false,
                'choice_label' => static function (SenderAddress $address): string {
                    $name = implode(' ', array_filter([
                        $address->getName1(),
                        $address->getName2(),
                        $address->getName3(),
                    ]));

                    return sprintf(
                        '%s, %s, %s %s %s',
                        $name,
                        $address->getStreet(),
                        $address->getCountryIsoCode(),
                        $address->getZipCode(),
                        $address->getCity()
                    );
                },
                'choice_loader' => new CallbackChoiceLoader(function () {
                    return $this->getAddresses();
                }),
                'label' => $this->getTranslator()->trans('Address', [], 'Modules.Glspoland.Configuration'),
            ]);
        }
    }

    public function getParent(): string
    {
        switch ($this->getStatus()) {
            case SenderAddressStatus::ANY:
                return AddressDetailsType::class;
            case SenderAddressStatus::FROM_DICTIONARY:
                return ChoiceType::class;
            default:
                throw new UnableToSelectSenderAddressException('Selecting a sender address is not allowed for the configured API account.');
        }
    }

    private function getStatus(): int
    {
        return $this->status ?? ($this->status = $this->client->getStatus()->getValue());
    }

    private function getAddresses(): SenderAddressesCollection
    {
        return $this->addresses ?? ($this->addresses = $this->client->getAvailableAddresses());
    }
}
