package ru.micord.ervu.security.esia.service;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.Base64;

import com.fasterxml.jackson.databind.ObjectMapper;
import ru.micord.ervu.security.esia.config.EsiaConfig;
import ru.micord.ervu.security.esia.model.EsiaAccessToken;
import ru.micord.ervu.security.esia.model.PassportModel;
import ru.micord.ervu.security.esia.model.PersonModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;

/**
 * @author Eduard Tihomirov
 */
@Service
public class EsiaPersonalDataService implements PersonalDataService {

  @Autowired
  private EsiaConfig esiaConfig;

  @Autowired
  private ObjectMapper objectMapper;

  @Override
  public PersonModel getPersonModel(String accessToken) {
    try {
      EsiaAccessToken esiaAccessToken = readToken(accessToken);
      String prnsId = esiaAccessToken.getSbj_id();
      PersonModel personModel = getPersonData(prnsId, accessToken);
      personModel.setPassportModel(
          getPassportModel(prnsId, accessToken, personModel.getrIdDoc()));
      personModel.setPrnsId(prnsId);
      return personModel;
    }
    catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  private PassportModel getPassportModel(String prsnId, String accessToken, String docId) {
    try {
      String url = esiaConfig.getEsiaBaseUri() + "rs/prns/" + prsnId;
      HttpRequest getReqDoc = HttpRequest.newBuilder(URI.create(url + "/docs/" + docId))
          .header(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded")
          .header("Authorization", "Bearer ".concat(accessToken))
          .GET()
          .timeout(Duration.ofSeconds(esiaConfig.getRequestTimeout()))
          .build();
      HttpResponse<String> getRespDoc = HttpClient.newBuilder()
          .connectTimeout(Duration.ofSeconds(esiaConfig.getConnectionTimeout()))
          .build()
          .send(getReqDoc, HttpResponse.BodyHandlers.ofString());

      return objectMapper.readValue(getRespDoc.body(), PassportModel.class);
    }
    catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  private PersonModel getPersonData(String prsnId, String accessToken) {
    try {
      String url = esiaConfig.getEsiaBaseUri() + "rs/prns/";
      HttpRequest getReq = HttpRequest.newBuilder(URI.create(url + prsnId))
          .header(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded")
          .header("Authorization", "Bearer ".concat(accessToken))
          .GET()
          .timeout(Duration.ofSeconds(esiaConfig.getRequestTimeout()))
          .build();
      HttpResponse<String> getResp = HttpClient.newBuilder()
          .connectTimeout(Duration.ofSeconds(esiaConfig.getConnectionTimeout()))
          .build()
          .send(getReq, HttpResponse.BodyHandlers.ofString());
      return objectMapper.readValue(getResp.body(), PersonModel.class);
    }
    catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  @Override
  public EsiaAccessToken readToken(String accessToken) {
    try {
      byte[] decodedBytes = Base64.getDecoder()
          .decode(
              accessToken.substring(accessToken.indexOf('.') + 1, accessToken.lastIndexOf('.'))
                  .replace('-', '+')
                  .replace('_', '/'));
      String decodedString = new String(decodedBytes);
      EsiaAccessToken esiaAccessToken = objectMapper.readValue(decodedString,
          EsiaAccessToken.class
      );
      return esiaAccessToken;
    }
    catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
}
