<?php declare(strict_types = 1);

namespace MailPoet\CustomFields\RestApi\Endpoints;

if (!defined('ABSPATH')) exit;


use MailPoet\API\REST\Request;
use MailPoet\API\REST\Response;
use MailPoet\CustomFields\CustomFieldsRepository;
use MailPoet\CustomFields\RestApi\CustomFieldApiException;
use MailPoet\Entities\CustomFieldEntity;
use MailPoet\Validator\Builder;
use MailPoetVendor\Doctrine\DBAL\Exception\UniqueConstraintViolationException;

class CustomFieldsDuplicateEndpoint extends CustomFieldsEndpoint {
  /** @var CustomFieldsRepository */
  private $customFieldsRepository;

  public function __construct(
    CustomFieldsRepository $customFieldsRepository
  ) {
    $this->customFieldsRepository = $customFieldsRepository;
  }

  public function handle(Request $request): Response {
    $customField = $this->customFieldsRepository->findOneById($this->getId($request));
    if (!$customField instanceof CustomFieldEntity || $customField->getDeletedAt() !== null) {
      throw new CustomFieldApiException(
        __('The custom field does not exist.', 'mailpoet'),
        404,
        'mailpoet_custom_fields_not_found'
      );
    }

    $params = $customField->getParams() ?: [];
    $params['label'] = $this->getDuplicateLabel($params, $customField->getName());

    $attempts = 0;
    while (true) {
      try {
        $duplicate = $this->customFieldsRepository->createOrUpdate([
          'name' => $this->getDuplicateName($customField->getName()),
          'type' => $customField->getType(),
          'params' => $params,
        ]);
        break;
      } catch (UniqueConstraintViolationException $exception) {
        // A concurrent duplicate request picked the same candidate name. Recompute and retry.
        if (++$attempts >= 3) {
          throw new CustomFieldApiException(
            __('A custom field with this name already exists.', 'mailpoet'),
            409,
            'mailpoet_custom_fields_duplicate',
            [],
            $exception
          );
        }
      }
    }

    return new Response($this->buildItem($duplicate), 201);
  }

  private function getId(Request $request): int {
    $rawId = $request->getParam('id');
    return (int)(is_scalar($rawId) ? $rawId : 0);
  }

  private function getDuplicateName(string $name): string {
    /* translators: %s is the original custom field name. */
    $candidate = sprintf(__('%s copy', 'mailpoet'), $name);
    $suffix = 2;
    while ($this->customFieldsRepository->findOneBy(['name' => $candidate]) instanceof CustomFieldEntity) {
      /* translators: %1$s is the original custom field name, %2$d is the copy number. */
      $candidate = sprintf(__('%1$s copy %2$d', 'mailpoet'), $name, $suffix);
      $suffix++;
    }
    return $candidate;
  }

  private function getDuplicateLabel(array $params, string $fallback): string {
    $label = isset($params['label']) && is_scalar($params['label']) ? (string)$params['label'] : $fallback;
    /* translators: %s is the original custom field label. */
    return sprintf(__('%s copy', 'mailpoet'), $label);
  }

  public static function getRequestSchema(): array {
    return [
      'id' => Builder::integer()->required(),
    ];
  }
}