package com.interplug.qcast.util;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.interplug.qcast.biz.user.dto.SyncResponse;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;

@Slf4j
@Component
@RequiredArgsConstructor
public class InterfaceQsp {

  @Value("${qsp.url}")
  private String qspUrl;

  /**
   * API Call
   *
   * @param apiPath
   * @param requestObject
   * @return String
   * @throws Exception
   */
  public String callApi(HttpMethod httpMethod, String apiPath, Object requestObject)
      throws Exception {

    URL url = null;
    HttpURLConnection con = null;
    OutputStreamWriter osw = null;
    BufferedReader br = null;
    StringBuilder sb = null;

    try {
      url = new URL(apiPath);
      con = (HttpURLConnection) url.openConnection();
      con.setConnectTimeout(30000); // 서버에 연결되는 Timeout 시간 설정
      con.setReadTimeout(30000); // InputStream 읽어 오는 Timeout 시간 설정
      con.setRequestMethod(httpMethod.toString());
      con.setRequestProperty("Content-Type", "application/json");
      con.setDoInput(true);
      con.setUseCaches(false);
      con.setDefaultUseCaches(false);

      if (HttpMethod.GET.equals(httpMethod)) {
        con.setDoOutput(false);
      } else {
        con.setDoOutput(true); // POST 데이터를 OutputStream으로 넘겨 주겠다는 설정
        if (requestObject != null) {
          ObjectMapper om = new ObjectMapper();
          osw = new OutputStreamWriter(con.getOutputStream());
          osw.write(om.writeValueAsString(requestObject)); // json 형식의 message 전달
          osw.flush();
        }
      }

      sb = new StringBuilder();
      if (con.getResponseCode() == HttpURLConnection.HTTP_OK) {
        br = new BufferedReader(new InputStreamReader(con.getInputStream(), "utf-8"));

        String line;
        while ((line = br.readLine()) != null) {
          sb.append(line);
        }
      }
    } catch (Exception e) {
      throw e;
    } finally {
      try {
        if (osw != null) osw.close();
        if (br != null) br.close();
      } catch (Exception e) {
        throw e;
      }
    }

    return sb != null ? sb.toString() : null;
  }

  /**
   * API 호출후 Response 객체로 Return
   *
   * @param method
   * @param qspInterUrl
   * @param requestObject
   * @param responseObject
   * @return
   * @throws Exception
   */
  public <T> T callApiData(
      HttpMethod method, String qspInterUrl, Object requestObject, Object responseObject)
      throws Exception {

    // 데이터 읽기
    SyncResponse syncResponse = new SyncResponse();

    String strResponse = this.callApi(method, qspUrl + qspInterUrl, requestObject);
    ObjectMapper om =
        new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

    String dataJson = "";
    if (!"".equals(strResponse)) {

      syncResponse = om.readValue(strResponse, SyncResponse.class);

      // 'data' 필드를 직접 사용하여 BatchResponse로 변환
      if (syncResponse.getData() != null) {
        dataJson = om.writeValueAsString(syncResponse.getData());
      }
    }
    if (responseObject == null) {
      return (T) dataJson;
    } else if (responseObject instanceof Class<?>) {
      return om.readValue(dataJson, (Class<T>) responseObject);
    } else if (responseObject instanceof TypeReference<?>) {
      return om.readValue(dataJson, (TypeReference<T>) responseObject);
    } else {
      throw new IllegalArgumentException("Unsupported responseType");
    }
  }

  /**
   * API Call - byte return, file down 사용
   *
   * @param apiPath
   * @param requestObject
   * @return String
   * @throws Exception
   */
  public byte[] callApi(
      HttpMethod httpMethod, String apiPath, Object requestObject, Map<String, String> result)
      throws Exception {

    URL url = null;
    HttpURLConnection con = null;
    OutputStreamWriter osw = null;
    BufferedReader br = null;

    byte[] bt = null;

    try {
      url = new URL(apiPath);
      con = (HttpURLConnection) url.openConnection();
      con.setConnectTimeout(30000); // 서버에 연결되는 Timeout 시간 설정
      con.setReadTimeout(30000); // InputStream 읽어 오는 Timeout 시간 설정
      con.setRequestMethod(httpMethod.toString());
      con.setRequestProperty("Content-Type", "application/json");
      con.setDoInput(true);
      con.setUseCaches(false);
      con.setDefaultUseCaches(false);

      if (HttpMethod.GET.equals(httpMethod)) {
        con.setDoOutput(false);
      } else {
        con.setDoOutput(true); // POST 데이터를 OutputStream으로 넘겨 주겠다는 설정
        if (requestObject != null) {
          ObjectMapper om = new ObjectMapper();
          osw = new OutputStreamWriter(con.getOutputStream());
          osw.write(om.writeValueAsString(requestObject)); // json 형식의 message 전달
          osw.flush();
        }
      }
      if (con.getResponseCode() == HttpURLConnection.HTTP_OK) {
        // response 헤더 값 결과 셋팅
        result.put("type", con.getHeaderField("Content-Type"));
        result.put("disposition", con.getHeaderField("Content-Disposition"));

        InputStream inputStream = con.getInputStream();
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        byte[] buffer = new byte[4096];
        int bytesRead;
        while ((bytesRead = inputStream.read(buffer)) != -1) {
          outputStream.write(buffer, 0, bytesRead);
        }
        bt = outputStream.toByteArray(); // 파일 데이터 반환
      }
    } catch (Exception e) {
      throw e;
    } finally {
      try {
        if (osw != null) osw.close();
        if (br != null) br.close();
      } catch (Exception e) {
        throw e;
      }
    }

    return bt != null ? bt : null;
  }
}
