<?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\Validator;

use InPost\International\Api\Shipment\Model\Parcel\DimensionUnit;
use InPost\International\Shipment\DTO\Dimensions;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Constraints\Range;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;

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

final class ParcelDimensionsValidator extends ConstraintValidator
{
    // global min/max dimensions
    private const MIN_DIMENSION_CM = 0.1;
    private const MAX_LENGTH_CM = 110.;
    private const MAX_WIDTH_CM = 60.;
    private const MAX_HEIGHT_CM = 60.;

    /**
     * @var PropertyAccessorInterface|null
     */
    private $propertyAccessor;

    public function __construct(PropertyAccessorInterface $propertyAccessor = null)
    {
        $this->propertyAccessor = $propertyAccessor;
    }

    public function validate($value, Constraint $constraint): void
    {
        if (!$constraint instanceof ParcelDimensions) {
            throw new UnexpectedTypeException($constraint, ParcelDimensions::class);
        }

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

        if (!$value instanceof Dimensions) {
            throw new UnexpectedTypeException($value, Dimensions::class);
        }

        $unit = $value->getUnit() ?? DimensionUnit::getDefault();

        $context = $this->context;
        $validator = $context->getValidator()->inContext($context);

        foreach ($this->getDimensionConstraints($unit) as $path => $dimensionConstraint) {
            $dimension = (float) $this->getPropertyAccessor()->getValue($value, $path);
            $validator->atPath($path)->validate($dimension, $dimensionConstraint, Constraint::DEFAULT_GROUP);
        }
    }

    private function getDimensionConstraints(DimensionUnit $unit): \Generator
    {
        $defaultUnit = DimensionUnit::getDefault();
        $min = $defaultUnit->convert(self::MIN_DIMENSION_CM, $unit);

        yield 'length' => new Range([
            'min' => $min,
            'max' => $defaultUnit->convert(self::MAX_LENGTH_CM, $unit),
        ]);
        yield 'width' => new Range([
            'min' => $min,
            'max' => $defaultUnit->convert(self::MAX_WIDTH_CM, $unit),
        ]);
        yield 'height' => new Range([
            'min' => $min,
            'max' => $defaultUnit->convert(self::MAX_HEIGHT_CM, $unit),
        ]);
    }

    private function getPropertyAccessor(): PropertyAccessorInterface
    {
        if (null === $this->propertyAccessor) {
            $this->propertyAccessor = PropertyAccess::createPropertyAccessor();
        }

        return $this->propertyAccessor;
    }
}
