<?php

declare(strict_types=1);

namespace Autogedal\AwbRetry\Plugin;

use Autogedal\AwbRetry\Model\AwbErrorLogManager;
use Autogedal\AwbRetry\Model\Config;
use Autogedal\AwbRetry\Model\Queue\RetryRequestPublisher;
use Autogedal\AwbRetry\Model\TransientFailureClassifier;
use Eadesigndev\Awb\Model\AwbRepository;
use Eadesigndev\Awb\Model\Sources\CarrierType;
use Psr\Log\LoggerInterface;

class CourierGenerateAwbRetryPlugin
{
    public function __construct(
        private readonly AwbRepository $awbRepository,
        private readonly AwbErrorLogManager $errorLogManager,
        private readonly RetryRequestPublisher $retryRequestPublisher,
        private readonly Config $config,
        private readonly TransientFailureClassifier $failureClassifier,
        private readonly LoggerInterface $logger
    ) {
    }

    public function aroundGenerateAwb(
        object $subject,
        callable $proceed,
        $awbId
    ): mixed {
        try {
            $result = $proceed($awbId);
            if (empty($result)) {
                throw new \RuntimeException((string) __('Empty AWB number returned by carrier.'));
            }
        } catch (\Throwable $throwable) {
            $awb = $this->awbRepository->getById((int) $awbId);
            $carrierId = (int) $awb->getData('carrier_id');
            $carrierCode = $this->resolveCarrierCode($carrierId);
            $log = $this->errorLogManager->upsertFailure(
                (int) $awbId,
                $carrierId,
                $carrierCode,
                $throwable,
                1,
                [
                    'source_class' => get_class($subject),
                    'method' => 'generateAwb',
                    'awb_id' => (int) $awbId,
                ]
            );

            if (
                $this->failureClassifier->isTransient($throwable)
                && (int) $log->getData('attempt_count') < $this->config->getMaxAttempts()
            ) {
                $this->retryRequestPublisher->queue(
                    (int) $log->getId(),
                    RetryRequestPublisher::RETRY_MODE_AUTOMATIC
                );
            }

            throw $throwable;
        }

        // A later cron/admin generation can succeed without going through RetryProcessor.
        // A log-write failure must not turn that success into another courier request.
        try {
            $awb = $this->awbRepository->getById((int) $awbId);
            $this->errorLogManager->resolveForAwb(
                (int) $awbId,
                (int) $awb->getData('carrier_id'),
                (string) $result
            );
        } catch (\Throwable $throwable) {
            $this->logger->error(
                'AWB generation succeeded, but retry log synchronization failed.',
                ['awb_id' => (int) $awbId, 'exception' => $throwable]
            );
        }

        return $result;
    }

    private function resolveCarrierCode(int $carrierId): string
    {
        return match ($carrierId) {
            CarrierType::PACKETA_SAMEDAY,
            CarrierType::PACKETA_FAN_COURIER => 'packeta',
            default => CarrierType::$carriersSettingsPath[$carrierId] ?? '',
        };
    }
}
