package zmo.technology.lite.core;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.serializer.SerializerFeature;

public class ZeroRequest<T> {

	private List<T> querys;
	private JSONObject expands;

	public ZeroRequest() {
		super();
		querys = new ArrayList<>();
		expands = new JSONObject();
	}

	public List<T> getQuerys() {
		return querys;
	}

	public ZeroRequest<T> setQuerys(List<T> querys) {
		this.querys = querys;
		return this;
	}

	public ZeroRequest<T> addQuery(T query) {
		this.querys.add(query);
		return this;
	}

	public ZeroRequest<T> removeQuery(int i) {
		this.querys.remove(i);
		return this;
	}

	public JSONObject getExpands() {
		return expands;
	}

	public ZeroRequest<T> setExpands(JSONObject expands) {
		this.expands = expands;
		return this;
	}

	public ZeroRequest<T> putExpands(String key, String value) {
		this.expands.put(key, value);
		return this;
	}

	public ZeroRequest<T> removeExpands(String key) {
		this.expands.remove(key);
		return this;
	}

	public String toJSONString() {
		return JSONObject.toJSONString(this);
	}

	public static ZeroRequest<?> build() {
		return new ZeroRequest<>();
	}

	public static <T> ZeroRequest<T> build(Class<T> type) {
		return new ZeroRequest<T>();
	}

	public static ZeroRequest<?> build(String json) {
		return JSONObject.parseObject(json, ZeroRequest.class);
	}

	public static ZeroRequest<?> build(byte[] jsonBytes) {
		return JSONObject.parseObject(new String(jsonBytes), ZeroRequest.class);
	}

	public static <T> ZeroRequest<T> build(Class<T> type, String json) throws IOException {
		if (json.length() <= 0 || json.charAt(0) != '{')
			throw new IOException("invalid json body");
		ZeroRequest<T> request = build(type);
		JSONObject jsonObject = JSONObject.parseObject(json);

		if (jsonObject.containsKey("querys")) {
			request.setQuerys(new ArrayList<>());
			JSONArray jsonArray = jsonObject.getJSONArray("querys");
			for (int i = 0; i < jsonArray.size(); i++)
				request.getQuerys()
						.add(JSONObject.parseObject(JSONObject.toJSONString(jsonArray.getJSONObject(i)), type));
		}

		if (jsonObject.containsKey("expands"))
			jsonObject.getJSONObject("expands").entrySet()
					.forEach(entry -> request.putExpands(entry.getKey(), entry.getValue().toString()));

		return request;
	}

	public static <T> ZeroRequest<T> build(Class<T> type, byte[] json) throws IOException {
		return build(type, new String(json));
	}

	public static <T> ZeroRequest<ZeroQuery<T>> build(String json, Class<T> type) throws IOException {
		if (json.length() <= 0 || json.charAt(0) != '{')
			throw new IOException("invalid json body");
		ZeroRequest<ZeroQuery<T>> request = new ZeroRequest<ZeroQuery<T>>();
		JSONObject jsonObject = JSONObject.parseObject(json);

		if (jsonObject.containsKey("querys")) {
			JSONArray jsonArray = jsonObject.getJSONArray("querys");

			for (int i = 0; i < jsonArray.size(); i++) {
				ZeroQuery<T> query = jsonArray.getJSONObject(i)
						.toJavaObject(new TypeReference<ZeroQuery<T>>() {
						});
				query.getTargets().clear();

				if (jsonArray.getJSONObject(i).containsKey("targets")) {
					JSONArray targets = jsonArray.getJSONObject(i).getJSONArray("targets");
					for (int j = 0; j < targets.size(); j++)
						query.getTargets().add(targets.getJSONObject(i).toJavaObject(type));
				}

				request.addQuery(query);
			}
		}

		if (jsonObject.containsKey("expands"))
			jsonObject.getJSONObject("expands").entrySet()
					.forEach(entry -> request.putExpands(entry.getKey(), entry.getValue().toString()));

		return request;
	}

	public static <T> ZeroRequest<ZeroQuery<T>> build(byte[] json, Class<T> type) throws IOException {
		return build(new String(json), type);
	}

	@Override
	public String toString() {
		return JSONObject.toJSONString(this, SerializerFeature.PrettyFormat);
	}

	public byte[] toBytes() {
		return toJSONString().getBytes(StandardCharsets.UTF_8);
	}

	public static final String EXPANDS_OPTIONS = "options";
	public static final String EXPANDS_OPTION_ALL = "all";
	public static final String EXPANDS_OPTION_BASIC = "basic";

	public boolean hasOption(String optionName) {

		return expands.containsKey(EXPANDS_OPTIONS)
				&& (expands.getString(EXPANDS_OPTIONS).contains(optionName)
						|| expands.getString(EXPANDS_OPTIONS).contains(EXPANDS_OPTION_ALL));
	}

	public boolean hasExpand(String expandName) {
		return expands.containsKey(expandName);
	}

	public boolean hasExpand(String expandName, String expandValue) {
		return hasExpand(expandName) && expands.getString(expandName).contains(expandValue);
	}

}
