<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\MainBundle\Templating\Entity;

use Chill\MainBundle\Entity\Address;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;

/**
 * @implements ChillEntityRenderInterface<Address>
 */
class AddressRender implements ChillEntityRenderInterface
{
    final public const DEFAULT_OPTIONS = [
        'with_valid_from' => false,
        'with_valid_to' => false,
        'with_picto' => false,
        'with_delimiter' => false,
        'has_no_address' => false,
        'multiline' => true,
        'separator' => ' — ',
        /* deprecated */
        'extended_infos' => false,
    ];

    public function __construct(private readonly \Twig\Environment $templating, private readonly TranslatableStringHelperInterface $translatableStringHelper) {}

    public function renderBox($addr, array $options): string
    {
        $options = \array_merge(self::DEFAULT_OPTIONS, $options);

        return $this->templating
            ->render('@ChillMain/Entity/address.html.twig', [
                'address' => $addr,
                'streetLine' => $this->renderStreetLine($addr),
                'render' => $options['render'] ?? 'bloc',
                'options' => $options,
                'lines' => $this->renderLines($addr),
            ]);
    }

    /**
     * @return string[]
     */
    public function renderLines(Address $addr, bool $includeCityLine = true, bool $includeCountry = true): array
    {
        $lines = [];

        if (null !== $addr->getPostcode()) {
            if ('FR' === $addr->getPostcode()->getCountry()->getCountryCode()) {
                $lines[] = $this->renderIntraBuildingLine($addr);
                $lines[] = $this->renderBuildingLine($addr);
                $lines[] = $this->renderStreetLine($addr);
                $lines[] = $this->renderDeliveryLine($addr);

                if ($includeCityLine) {
                    $lines[] = $this->renderCityLine($addr);
                }

                if ($includeCountry) {
                    $lines[] = $this->renderCountryLine($addr);
                }
            } else {
                $lines[] = $this->renderBuildingLine($addr);
                $lines[] = $this->renderDeliveryLine($addr);
                $lines[] = $this->renderStreetLine($addr);

                if ($includeCityLine) {
                    $lines[] = $this->renderCityLine($addr);
                }

                if ($includeCountry) {
                    $lines[] = $this->renderCountryLine($addr);
                }
            }
        }

        return array_values(array_filter($lines, static fn ($l) => '' !== (string) $l));
    }

    public function renderStreetLine(Address $addr): ?string
    {
        if ('' !== $addr->getStreet()) {
            $street = $addr->getStreet();
        } else {
            $street = '';
        }

        if ('' !== $addr->getStreetNumber()) {
            $streetNumber = $addr->getStreetNumber();
        } else {
            $streetNumber = '';
        }

        $res = trim($street.', '.$streetNumber, ', ');

        if ('FR' === $addr->getPostcode()->getCountry()->getCountryCode()) {
            $res = trim($streetNumber.', '.$street, ', ');
        }

        if ((',' === $res) || ('' === $res)) {
            $res = null;
        }

        return $res;
    }

    public function renderString($addr, array $options): string
    {
        $opts = [...self::DEFAULT_OPTIONS, ...$options];

        return implode($opts['separator'], $this->renderLines($addr));
    }

    public function supports($entity, array $options): bool
    {
        return $entity instanceof Address;
    }

    private function renderBuildingLine(Address $addr): ?string
    {
        if ('' !== $addr->getBuildingName()) {
            $building = $addr->getBuildingName();
        } else {
            $building = '';
        }
        $intraBuilding = $this->renderIntraBuildingLine($addr);

        if (null === $intraBuilding) {
            $intraBuilding = '';
        }

        $res = trim($building.' - '.$intraBuilding, ' - ');

        if ('' === $res) {
            $res = null;
        }

        if ('FR' === $addr->getPostcode()->getCountry()->getCountryCode()) {
            $res = $addr->getBuildingName();
        }

        return $res;
    }

    private function renderCityLine(Address $addr): string
    {
        if (null !== $addr->getPostcode()) {
            $res = \strtr('{postcode} {label}', [
                '{postcode}' => $addr->getPostcode()->getCode(),
                '{label}' => $addr->getPostcode()->getName(),
            ]);

            if ('FR' === $addr->getPostcode()->getCountry()->getCountryCode()) {
                if ('' !== $addr->getDistribution()) {
                    $res = $res.' '.$addr->getDistribution();
                }
            }
        }

        return $res ?? '';
    }

    private function renderCountryLine(Address $addr): ?string
    {
        return $this->translatableStringHelper->localize(
            $addr->getPostcode()->getCountry()->getName()
        );
    }

    private function renderDeliveryLine(Address $addr): string
    {
        return $addr->getExtra();
    }

    private function renderIntraBuildingLine(Address $addr): ?string
    {
        $arr = [];

        if ('' !== $addr->getFlat()) {
            $arr[] = 'appart '.$addr->getFlat();
        }

        if ('' !== $addr->getFloor()) {
            $arr[] = 'ét '.$addr->getFloor();
        }

        if ('' !== $addr->getCorridor()) {
            $arr[] = 'coul '.$addr->getCorridor();
        }

        if ('' !== $addr->getSteps()) {
            $arr[] = 'esc '.$addr->getSteps();
        }

        $res = implode(' - ', $arr);

        if ('' === $res) {
            $res = null;
        }

        return $res;
    }
}
