package service;

import java.lang.invoke.MethodHandles;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Base64;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import dto.ConfigExecuteRequest;
import dto.ExportDataRequest;
import model.FileModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.StreamUtils;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import org.springframework.core.io.Resource;
import ru.micord.ervu_eks.exception.ConfigExecutorException;

/**
 * @author Evgenii Malkov
 */
@Service
public class ConfigExecutorService {

  private static final Logger LOGGER = LoggerFactory.getLogger(
      MethodHandles.lookup().lookupClass());
  private final RestTemplate restTemplate;
  private final String url;

  public ConfigExecutorService(@Autowired RestTemplate restTemplate,
                               @Value("${config.data.executor.url}") String url) {
    this.restTemplate = restTemplate;
    this.url = url;
  }

  public FileModel exportData(ExportDataRequest request) {
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    HttpEntity<ExportDataRequest> entity = new HttpEntity<>(request, headers);

    try {
      ResponseEntity<Resource> response = restTemplate.exchange(
          url.concat("/").concat("downloadCSV"), HttpMethod.POST, entity, Resource.class);
      String content = "";
      if (response.getBody() != null) {
        byte[] fileBytes = StreamUtils.copyToByteArray(response.getBody().getInputStream());
        content = Base64.getEncoder().encodeToString(fileBytes);
      }
      String fileExtension = ".csv";
      List<String> contentDisposition = response.getHeaders().get(HttpHeaders.CONTENT_DISPOSITION);
      if (contentDisposition != null && !contentDisposition.isEmpty()) {
        String disposition = contentDisposition.get(0);
        int idx = disposition.indexOf("filename=");
        if (idx != -1) {
          String fileNameFromHeader = disposition.substring(idx + 9).replace("\"", "");
          int dotIndex = fileNameFromHeader.lastIndexOf(".");
          if (dotIndex != -1) {
            fileExtension = fileNameFromHeader.substring(dotIndex);
          }
        }
      }
      String fileName =
          request.getType() + "_" + new SimpleDateFormat("dd.MM.yyyy").format(new Date())
          + fileExtension;
      FileModel fileModel = new FileModel();
      fileModel.setFileContent(content);
      fileModel.setFileExtension(fileExtension);
      fileModel.setFileName(fileName);
      return fileModel;
    }
    catch (HttpClientErrorException e) {
      if (e.getStatusCode() == HttpStatus.BAD_REQUEST) {
        Map<String, Object> responseMap = new Gson().fromJson(e.getResponseBodyAsString(),
            new TypeToken<Map<String, Object>>() {
            }.getType()
        );

        if (responseMap.get("details") instanceof Map) {
          @SuppressWarnings("unchecked")
          Map<String, String> details = (Map<String, String>) responseMap.get("details");
          String detailsStr = String.join("\n", details.values());
          throw new ConfigExecutorException("eks.error.misc", new RuntimeException(detailsStr, e), detailsStr);
        }

        throw new ConfigExecutorException((String) responseMap.get("details"), e);
      }
      else {
        throw new RuntimeException("Export data failed with error", e);
      }
    }
    catch (Exception e) {
      throw new RuntimeException("Export data failed with error", e);
    }
  }

  public String call(String methodPath, ConfigExecuteRequest configExecuteRequest,
                     boolean withDate) {
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    HttpEntity<?> entity;
    if (withDate) {
      entity = new HttpEntity<>(configExecuteRequest, headers);
    }
    else {
      entity = new HttpEntity<>(configExecuteRequest.getIds(), headers);
    }
    LOGGER.info("Starts call config executor service with method: {}, for request: {}", methodPath,
        configExecuteRequest
    );
    try {
      ResponseEntity<String> response = restTemplate.exchange(url.concat(methodPath),
          HttpMethod.POST, entity, String.class
      );
      LOGGER.info("Method: {}, executed with status: {}, for request:{}", methodPath,
          response.getStatusCode().value(), configExecuteRequest
      );
      return response.getBody();
    }
    catch (HttpClientErrorException e) {

      if (e.getStatusCode() == HttpStatus.BAD_REQUEST) {
        Map<String, Object> responseMap = new Gson().fromJson(e.getResponseBodyAsString(),
            new TypeToken<Map<String, Object>>() {
            }.getType()
        );

        if (responseMap.get("details") instanceof Map) {
          @SuppressWarnings("unchecked")
          Map<String, String> details = (Map<String, String>) responseMap.get("details");
          String detailsStr = String.join("\n", details.values());
          throw new ConfigExecutorException("eks.error.misc", new RuntimeException(detailsStr, e), detailsStr);
        }

        throw new ConfigExecutorException((String) responseMap.get("details"), e);
      }
      else {
        throw new RuntimeException(
            String.format("Failed call config executor service method: %s for request: %s with error",
                methodPath, configExecuteRequest
            ), e);
      }
    }
    catch (Exception e) {
      throw new RuntimeException(
          String.format("Failed call config executor service method: %s for request: %s with error",
              methodPath, configExecuteRequest
          ), e);
    }
  }

  public List<String> getExportDataIds() {
    ResponseEntity<String[]> listDownloadTypes = restTemplate.getForEntity(
        url.concat("/").concat("listDownloadTypes"), String[].class);
    return Arrays.stream(Objects.requireNonNull(listDownloadTypes.getBody())).toList();
  }
}
