package ru.ibs.socrates.confexporter.service.content.utils;

import feign.Response;
import feign.RetryableException;
import feign.codec.ErrorDecoder;


/**
 * Кастомный декодер ошибок для Feign, который преобразует определенные HTTP-ошибки
 * в {@link RetryableException} для последующих повторных попыток (retry).
 * <p>
 * Основная логика:
 * <ul>
 *   <li>Ошибки 5xx (серверные) и 429 (Too Many Requests) помечаются как повторяемые</li>
 *   <li>Все остальные ошибки обрабатываются стандартным декодером Feign</li>
 * </ul>
 *
 * @see feign.codec.ErrorDecoder
 * @see feign.RetryableException
 */
public class CustomErrorDecoder implements ErrorDecoder {

  /**
   * Стандартный декодер ошибок Feign (используется как fallback).
   */
  private final ErrorDecoder defaultErrorDecoder = new Default();

  /**
   * Декодирует HTTP-ответ в исключение, определяя, требуется ли повторная попытка.
   *
   * @param methodKey ключ метода Feign (например, "ServiceName#methodName(paramType)")
   * @param response HTTP-ответ, содержащий статус и заголовки
   * @return исключение для выброса:
   *         <ul>
   *           <li>{@link RetryableException} — для ошибок 5xx и 429 (требует retry)</li>
   *           <li>Стандартное исключение Feign — для остальных случаев</li>
   *         </ul>
   *
   * @throws RetryableException если ответ содержит статус 5xx или 429
   * @see feign.FeignException
   */
  @Override
  public Exception decode(String methodKey, Response response) {
    var exception = defaultErrorDecoder.decode(methodKey, response);
    
    // Серверные ошибки (5xx) и 429 (Too Many Requests) делаем ретраебельными
    if (response.status() >= 500 && response.status() < 600
        || response.status() == 429) {
      return new RetryableException(
          response.status(),
          exception.getMessage(),
          response.request().httpMethod(),
          exception,
          (Long) null,  // Таймаут retry (null = используется значение по умолчанию)
          response.request());
    } else {
      // Все остальные ошибки обрабатываем стандартно
      return exception;
    }
  }
}