package ru.ibs.socrates.confexporter.external.access.config;

import feign.Client;
import feign.Request;
import feign.Retryer;
import feign.codec.ErrorDecoder;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import lombok.SneakyThrows;
import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
import org.apache.hc.client5.http.ssl.TrustAllStrategy;
import org.apache.hc.core5.ssl.SSLContexts;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.util.ResourceUtils;
import ru.ibs.socrates.confexporter.service.content.utils.CustomErrorDecoder;
import ru.ibs.socrates.confexporter.service.content.utils.LimitInterceptor;


public class AccessClientConfig {

  @Value("${integrations.sdt-access.ssl.key-store-enabled}")
  private Boolean keyStoreEnabled;

  @Value("${integrations.sdt-access.ssl.key-store-path}")
  private String keyStorePassword;

  @Value("${integrations.sdt-access.ssl.key-store-password}")
  private String keyStorePath;

  @Value("${integrations.sdt-access.ssl.trust-store-enabled}")
  private Boolean trustStoreEnabled;

  @Value("${integrations.sdt-access.ssl.trust-store-path}")
  private String trustStorePassword;

  @Value("${integrations.sdt-access.ssl.trust-store-password}")
  private String trustStorePath;


  @Bean
  public Client client() {
    return new Client.Default(getSslSocketFactory(), new NoopHostnameVerifier(), false);
  }

  @SneakyThrows
  private SSLSocketFactory getSslSocketFactory() {
    final SSLContext sslContext;
    if (trustStoreEnabled) {
      if (keyStoreEnabled) {
        sslContext = SSLContexts.custom()
            .loadTrustMaterial(ResourceUtils.getFile(trustStorePath),
                trustStorePassword.toCharArray())
            .loadKeyMaterial(ResourceUtils.getFile(keyStorePath), keyStorePassword.toCharArray(),
                keyStorePassword.toCharArray())
            .build();
      } else {
        sslContext = SSLContexts.custom()
            .loadTrustMaterial(ResourceUtils.getFile(trustStorePath),
                trustStorePassword.toCharArray())
            .build();
      }
    } else {
      if (keyStoreEnabled) {
        sslContext = SSLContexts.custom()
            .loadTrustMaterial(null, new TrustAllStrategy())
            .loadKeyMaterial(ResourceUtils.getFile(keyStorePath), keyStorePassword.toCharArray(),
                keyStorePassword.toCharArray())
            .build();
      } else {
        sslContext = SSLContexts.custom()
            .loadTrustMaterial(null, new TrustAllStrategy())
            .build();
      }
    }
    return sslContext.getSocketFactory();
  }

  @Bean
  @ConditionalOnProperty(name = "integrations.sdt-access.limit.enabled")
  ErrorDecoder errorDecoder() {
    return new CustomErrorDecoder();
  }

  @Bean
  LimitInterceptor limitRequestInterceptor(
      @Value("${integrations.sdt-access.limit.per-second}") Long limit) {
    return new LimitInterceptor(limit);
  }

  @Bean
  @ConditionalOnProperty(name = "integrations.sdt-access.retry.enabled", matchIfMissing = false)
  Retryer retryer(
      @Value("${integrations.sdt-access.retry.period}") Long period,
      @Value("${integrations.sdt-access.retry.max-period}") Long maxPeriod,
      @Value("${integrations.sdt-access.retry.max-attempts}") Integer maxAttempts) {
    return new Retryer.Default(period, maxPeriod, maxAttempts);
  }

  @Bean
  Request.Options options(
      @Value("${integrations.sdt-access.connect-timeout}") long connectTimeout,
      @Value("${integrations.sdt-access.read-timeout}") long readTimeout) {
    return new Request.Options(connectTimeout, TimeUnit.MILLISECONDS, readTimeout,
        TimeUnit.MILLISECONDS, true);
  }


}
