<?php

declare(strict_types=1);

namespace CpCreation\VitiCore\Bridge\Symfony\Bundle\Form\Site\Account\Model;

use CpCreation\VitiCore\Address\BusinessIdentifierRules;
use CpCreation\VitiCore\Behavior\HasAddress;
use CpCreation\VitiCore\User\Model\DeliveryAddress;
use CpCreation\VitiCore\User\Model\InvoiceAddress;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;

final class AccountAddressData
{
    public bool $differentAddress = false;

    #[Assert\Email(message: 'security.email.invalid')]
    public ?string $email = null;

    public ?string $company = null;

    public ?string $siret = null;

    public ?string $vatId = null;

    public ?string $firstname = null;

    public ?string $lastname = null;

    public ?string $street = null;

    public ?string $postal = null;

    public ?string $city = null;

    public ?string $country = null;

    public ?string $phone = null;

    public static function fromAddress(?HasAddress $address): self
    {
        $data = new self();

        if (null === $address) {
            return $data;
        }

        if (method_exists($address, 'getEmail')) {
            $data->email = $address->getEmail();
        }

        $data->company = $address->getCompany();
        $data->siret = $address->getSiret();
        $data->vatId = $address->getVatId();
        $data->firstname = $address->getFirstname();
        $data->lastname = $address->getLastname();
        $data->street = $address->getStreet();
        $data->postal = $address->getPostal();
        $data->city = $address->getCity();
        $data->country = $address->getCountry();
        $data->phone = $address->getPhone();

        return $data;
    }

    public function toInvoiceAddress(?InvoiceAddress $address): ?InvoiceAddress
    {
        if ($this->isEmpty()) {
            return null;
        }

        $address ??= new InvoiceAddress();

        return $this->fill($address);
    }

    public function toDeliveryAddress(?DeliveryAddress $address): ?DeliveryAddress
    {
        if ($this->isEmpty()) {
            return null;
        }

        $address ??= new DeliveryAddress();

        return $this->fill($address);
    }

    private function fill(InvoiceAddress|DeliveryAddress $address): InvoiceAddress|DeliveryAddress
    {
        $country = BusinessIdentifierRules::normalizeCountry($this->country);

        if ($address instanceof InvoiceAddress) {
            $address->setEmail($this->normalizeEmail($this->email));
        }

        $address->setCompany($this->normalize($this->company));
        $address->setSiret(BusinessIdentifierRules::requiresSiret($country) ? BusinessIdentifierRules::normalizeIdentifier($this->siret) : null);
        $address->setVatId(BusinessIdentifierRules::normalizeIdentifier($this->vatId));
        $address->setFirstname($this->normalize($this->firstname));
        $address->setLastname($this->normalize($this->lastname));
        $address->setStreet($this->normalize($this->street));
        $address->setPostal($this->normalize($this->postal));
        $address->setCity($this->normalize($this->city));
        $address->setCountry($country);
        $address->setPhone($this->normalize($this->phone));

        return $address;
    }

    public function isEmpty(): bool
    {
        foreach ([
            $this->email,
            $this->company,
            $this->siret,
            $this->vatId,
            $this->firstname,
            $this->lastname,
            $this->street,
            $this->postal,
            $this->city,
            $this->country,
            $this->phone,
        ] as $value) {
            if (null !== $this->normalize($value)) {
                return false;
            }
        }

        return true;
    }

    public function equals(self $other): bool
    {
        return $this->normalizeEmail($this->email) === $this->normalizeEmail($other->email)
            && $this->normalize($this->company) === $this->normalize($other->company)
            && $this->normalize($this->siret) === $this->normalize($other->siret)
            && $this->normalize($this->vatId) === $this->normalize($other->vatId)
            && $this->normalize($this->firstname) === $this->normalize($other->firstname)
            && $this->normalize($this->lastname) === $this->normalize($other->lastname)
            && $this->normalize($this->street) === $this->normalize($other->street)
            && $this->normalize($this->postal) === $this->normalize($other->postal)
            && $this->normalize($this->city) === $this->normalize($other->city)
            && $this->normalizeCountry($this->country) === $this->normalizeCountry($other->country)
            && $this->normalize($this->phone) === $this->normalize($other->phone);
    }

    #[Assert\Callback]
    public function validate(ExecutionContextInterface $context): void
    {
        if (null !== BusinessIdentifierRules::normalizeIdentifier($this->vatId) && !BusinessIdentifierRules::hasCompany($this->company)) {
            $context
                ->buildViolation('This value should not be blank.')
                ->atPath('company')
                ->addViolation();
        }

        if (null !== BusinessIdentifierRules::normalizeIdentifier($this->siret) && !BusinessIdentifierRules::hasCompany($this->company)) {
            $context
                ->buildViolation('This value should not be blank.')
                ->atPath('company')
                ->addViolation();
        }

        if (!BusinessIdentifierRules::isValidVatId($this->vatId, $this->country)) {
            $context
                ->buildViolation('address.vat_id.invalid')
                ->atPath('vatId')
                ->addViolation();
        }

        if (!BusinessIdentifierRules::isValidSiret($this->siret)) {
            $context
                ->buildViolation('address.siret.invalid')
                ->atPath('siret')
                ->addViolation();
        }

        if (BusinessIdentifierRules::requiresSiretForCompany($this->country, $this->company) && null === BusinessIdentifierRules::normalizeIdentifier($this->siret)) {
            $context
                ->buildViolation('This value should not be blank.')
                ->atPath('siret')
                ->addViolation();
        }
    }

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

        $value = mb_trim($value);

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

    private function normalizeCountry(?string $value): ?string
    {
        return BusinessIdentifierRules::normalizeCountry($value);
    }

    private function normalizeEmail(?string $value): ?string
    {
        $value = $this->normalize($value);

        return null !== $value ? mb_strtolower($value) : null;
    }
}
