<?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\Delivery\Point;

use InPost\International\Api\Exception\ApiExceptionInterface;
use InPost\International\Api\Exception\ClientException;
use InPost\International\Api\Point\PointsApiClientInterface;
use Psr\Log\LoggerInterface;

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

/**
 * @todo cache point data
 */
final class PointDataProvider implements PointDataProviderInterface
{
    /**
     * @var PointsApiClientInterface
     */
    private $client;

    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * @var array<string, PointInterface|null> points cache by ID
     */
    private $points = [];

    public function __construct(PointsApiClientInterface $client, LoggerInterface $logger)
    {
        $this->client = $client;
        $this->logger = $logger;
    }

    public function getPoint(string $pointId): ?PointInterface
    {
        if (array_key_exists($pointId, $this->points)) {
            return $this->points[$pointId];
        }

        try {
            return $this->points[$pointId] = $this->client->getPointById($pointId);
        } catch (ApiExceptionInterface $e) {
            // TODO: check for specific exception based on API error code if they're ever implemented
            if (!in_array($e->getStatusCode(), [400, 404], true)) {
                throw $e;
            }

            return $this->points[$pointId] = null;
        } catch (ClientException $e) {
            return $this->points[$pointId] = null;
        } catch (\Throwable $e) {
            $this->handleException($e, $pointId);
        }
    }

    /**
     * @return never-returns
     */
    private function handleException(\Throwable $e, string $pointId): void
    {
        if (!$e instanceof ApiExceptionInterface) {
            $this->logger->critical('Could not fetch point "{pointId}" data: {exception}', [
                'pointId' => $pointId,
                'exception' => $e,
            ]);
        }

        throw $e;
    }
}
