package com.arialyy.frame.http;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import com.arialyy.frame.cache.CacheUtil;
import com.arialyy.frame.http.inf.IResponse;
import com.arialyy.frame.util.show.L;
import java.io.BufferedInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.Headers;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

/**
 * Created by lyy on 2015/11/5.
 * 网络连接工具
 */
public class HttpUtil {
  private static final String TAG = "HttpUtil";
  private Context mContext = null;
  private static volatile HttpUtil mUtil = null;
  private static final Object LOCK = new Object();
  private CacheUtil mCacheUtil;
  private Handler mHandler;
  private static final int TIME_OUT = 5000;
  public static final String CONTENT_TYPE_IMG = "image/*";
  public static final String CONTENT_TYPE_TEXT = "text/*";
  public static final String CONTENT_TYPE_FILE = "application/octet-stream";

  private HttpUtil() {
  }

  private HttpUtil(Context context) {
    mContext = context;
    mCacheUtil = new CacheUtil(mContext, false);
    mHandler = new Handler(Looper.getMainLooper());
  }

  public static HttpUtil getInstance(Context context) {
    if (mUtil == null) {
      synchronized (LOCK) {
        if (mUtil == null) {
          mUtil = new HttpUtil(context);
        }
      }
    }
    return mUtil;
  }

  public void get(final @NonNull String url, @NonNull final IResponse absResponse) {
    get(url, null, absResponse, false);
  }

  public void get(final @NonNull String url, @NonNull final IResponse absResponse,
      boolean useCache) {
    get(url, null, absResponse, useCache);
  }

  public void get(final @NonNull String url, final Map<String, String> params,
      @NonNull final IResponse absResponse) {
    get(url, params, absResponse, false);
  }

  public void post(final @NonNull String url, @NonNull final IResponse absResponse) {
    post(url, null, null, absResponse, false);
  }

  public void post(final @NonNull String url, final Map<String, String> params,
      @NonNull final IResponse absResponse) {
    post(url, params, null, absResponse, false);
  }

  public void post(final @NonNull String url, final Map<String, String> params,
      @NonNull final IResponse absResponse, final boolean useCache) {
    post(url, params, null, absResponse, useCache);
  }

  public void post(final @NonNull String url, final Map<String, String> params,
      final Map<String, String> header, @NonNull final IResponse absResponse) {
    post(url, params, header, absResponse, false);
  }

  /**
   * 上传文件
   *
   * @param key 上传文件键值
   */
  public void uploadFile(@NonNull final String url, @NonNull final String filePath,
      @NonNull final String key,
      final String contentType, final Map<String, String> header,
      @NonNull final IResponse absResponse) {
    new Thread(new Runnable() {
      @Override
      public void run() {
        File file = new File(filePath);
        String BOUNDARY = UUID.randomUUID().toString(); // 边界标识 随机生成
        String PREFIX = "--", LINE_END = "\r\n";
        String CONTENT_TYPE = "multipart/form-data"; // 内容类型
        try {
          HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
          conn.setReadTimeout(5000);
          conn.setConnectTimeout(5000);
          conn.setDoInput(true);
          conn.setDoOutput(true);
          conn.setUseCaches(false);
          conn.setRequestMethod("POST");
          conn.setRequestProperty("Charset", "utf-8"); // 设置编码
          conn.setRequestProperty("connection", "keep-alive");
          conn.setRequestProperty("Content-Type", CONTENT_TYPE + ";boundary=" + BOUNDARY);

          if (header != null && header.size() > 0) {
            Set set = header.entrySet();
            for (Object aSet : set) {
              Map.Entry entry = (Map.Entry) aSet;
              conn.setRequestProperty(entry.getKey() + "", entry.getValue() + "");
            }
          }
          OutputStream outputSteam = conn.getOutputStream();
          DataOutputStream dos = new DataOutputStream(outputSteam);
          StringBuilder sb = new StringBuilder();
          sb.append(PREFIX);
          sb.append(BOUNDARY);
          sb.append(LINE_END);
          sb.append("Content-Disposition: form-data; name=\"")
              .append(key)
              .append("\"; filename=\"")
              .append(file.getName())
              .append("\"")
              .append(LINE_END);
          sb.append("Content-Type:").append(contentType).append("; charset=utf-8").append(LINE_END);
          sb.append(LINE_END);
          dos.write(sb.toString().getBytes());
          InputStream is = new FileInputStream(file);
          byte[] bytes = new byte[1024];
          int len = 0;
          while ((len = is.read(bytes)) != -1) {
            dos.write(bytes, 0, len);
          }
          is.close();
          dos.write(LINE_END.getBytes());
          byte[] end_data = (PREFIX + BOUNDARY + PREFIX + LINE_END).getBytes();
          dos.write(end_data);
          dos.flush();
          int res = conn.getResponseCode();
          if (res == 200) {
            BufferedInputStream inputStream = new BufferedInputStream(conn.getInputStream());
            byte[] buf = new byte[1024];
            StringBuilder stringBuilder = new StringBuilder();
            while (inputStream.read(buf) > 0) {
              stringBuilder.append(new String(buf, 0, buf.length));
            }
            String data = stringBuilder.toString();
            L.j(data);
            absResponse.onResponse(data);
          } else {
            absResponse.onError("error");
          }
        } catch (MalformedURLException e) {
          e.printStackTrace();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }).start();
  }

  /**
   * 基本get方法
   */
  public void get(final @NonNull String url, final Map<String, String> params,
      @NonNull final IResponse absResponse, final boolean useCache) {
    L.v(TAG, "请求链接 >>>> " + url);
    String requestUrl = url;
    if (params != null && params.size() > 0) {
      Set set = params.entrySet();
      int i = 0;
      requestUrl += "?";
      for (Object aSet : set) {
        i++;
        Map.Entry entry = (Map.Entry) aSet;
        requestUrl += entry.getKey() + "=" + entry.getValue() + (i < params.size() ? "&" : "");
      }
      L.v(TAG, "请求参数为 >>>> ");
      L.m(params);
    }

    OkHttpClient client = new OkHttpClient.Builder().connectTimeout(TIME_OUT, TimeUnit.MILLISECONDS)
        .writeTimeout(TIME_OUT, TimeUnit.MILLISECONDS)
        .readTimeout(300000, TimeUnit.MILLISECONDS)
        .build();
    final Request request = new Request.Builder().url(requestUrl).build();
    Call call = client.newCall(request);

    //请求加入调度
    call.enqueue(new Callback() {
      @Override
      public void onFailure(Call call, IOException e) {
        L.e(TAG, "请求链接【" + url + "】失败");
        String data = null;
        if (useCache) {
          data = mCacheUtil.getStringCache(url + L.m2s(params));
          L.d(TAG, "数据获取成功，获取到的数据为 >>>> ");
          L.j(data);
        }
        if (TextUtils.isEmpty(data)) {
          setOnError(request, absResponse);
        } else {
          setOnResponse(data, absResponse);
        }
      }

      @Override
      public void onResponse(Call call, Response response) throws IOException {
        String data = response.body().string();
        L.d(TAG, "数据获取成功，获取到的数据为 >>>> ");
        L.j(data);
        if (useCache) {
          L.v(TAG, "缓存链接【" + url + "】的数据");
          mCacheUtil.putStringCache(url + L.m2s(params), data);
        }
        setOnResponse(data, absResponse);
      }
    });
  }

  /**
   * 基本的Post方法
   */
  public void post(final @NonNull String url, final Map<String, String> params,
      final Map<String, String> header, @NonNull final IResponse absResponse,
      final boolean useCache) {
    L.v(TAG, "请求链接 >>>> " + url);
    OkHttpClient client = new OkHttpClient.Builder().connectTimeout(TIME_OUT, TimeUnit.MILLISECONDS)
        .writeTimeout(TIME_OUT, TimeUnit.MILLISECONDS)
        .readTimeout(300000, TimeUnit.MILLISECONDS)
        .build();
    FormBody.Builder formB = new FormBody.Builder();
    //头数据
    Headers.Builder hb = new Headers.Builder();
    if (header != null && header.size() > 0) {
      Set set = header.entrySet();
      for (Object aSet : set) {
        Map.Entry entry = (Map.Entry) aSet;
        hb.add(entry.getKey() + "", entry.getValue() + "");
      }
      L.v(TAG, "请求的头数据为 >>>> ");
      L.m(header);
    }
    //请求参数
    if (params != null && params.size() > 0) {
      Set set = params.entrySet();
      for (Object aSet : set) {
        Map.Entry entry = (Map.Entry) aSet;
        formB.add(entry.getKey() + "", entry.getValue() + "");
      }
      L.v(TAG, "请求参数为 >>>> ");
      L.m(params);
    } else {
      formB.add("null", "null");
    }

    Request request =
        new Request.Builder().url(url).post(formB.build()).headers(hb.build()).build();
    Call call = client.newCall(request);
    call.enqueue(new Callback() {
      @Override
      public void onFailure(Call call, IOException e) {
        L.e(TAG, "请求链接【" + url + "】失败");
        String data = null;
        if (useCache) {
          data = mCacheUtil.getStringCache(url + L.m2s(params));
          L.d(TAG, "从缓存读取的数据为 >>>> ");
          L.j(data);
        }
        if (TextUtils.isEmpty(data)) {
          setOnError(call, absResponse);
        } else {
          setOnResponse(data, absResponse);
        }
      }

      @Override
      public void onResponse(Call call, Response response) throws IOException {
        String data = response.body().string();
        L.d(TAG, "数据获取成功，获取到的数据为 >>>>");
        L.j(data);
        if (useCache) {
          L.v(TAG, "缓存链接【" + url + "】的数据");
          mCacheUtil.putStringCache(url + L.m2s(params), data);
        }
        setOnResponse(data, absResponse);
      }
    });
  }

  private void setOnError(final Object error, final IResponse response) {
    mHandler.post(new Runnable() {
      @Override
      public void run() {
        response.onError(error);
      }
    });
  }

  private void setOnResponse(final String data, final IResponse response) {
    mHandler.post(new Runnable() {
      @Override
      public void run() {
        response.onResponse(data);
      }
    });
  }

  /**
   * 返回String类型的响应
   */
  public static class AbsResponse implements IResponse {

    @Override
    public void onResponse(String data) {

    }

    @Override
    public void onError(Object error) {

    }
  }
}
