package com.jasamedika.medifirst2000.controller;

import com.jasamedika.medifirst2000.base.vo.BaseModelVO;
import com.jasamedika.medifirst2000.controller.base.LocaleController;
import com.jasamedika.medifirst2000.exception.ServiceVOException;
import com.jasamedika.medifirst2000.service.ModelService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.persistence.Column;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import static com.jasamedika.medifirst2000.util.JsonUtil.ToMaps;
import static com.jasamedika.medifirst2000.util.rest.RestUtil.getJsonResponse;
import static java.net.URLDecoder.decode;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.OK;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.web.bind.annotation.RequestMethod.GET;

@RestController
@RequestMapping("/service")
public class GenericServiceController extends LocaleController<BaseModelVO> {

	private static final String CONTENT_TYPE = "Content-Type";

	@Autowired
	private ModelService<?> modelService;

	@RequestMapping(value = "/get-setting/", method = GET, produces = APPLICATION_JSON_VALUE)
	public ResponseEntity<String> getData(@RequestParam(value = "prefix", required = false) String prefix) {
		return getJsonResponse(GetSettingDataFixed(prefix), CREATED, mapHeaderMessage);
	}

	@RequestMapping(value = "/list-generic/", method = GET, produces = APPLICATION_JSON_VALUE)
	public ResponseEntity<List<Map<String, Object>>> listGeneric(
			@RequestParam(value = "view", required = false) String entity,
			@RequestParam(value = "select", required = false) String field,
			@RequestParam(value = "take", required = false) Integer take,
			@RequestParam(value = "skip", required = false) Integer skip,
			@RequestParam(value = "page", required = false) Integer page,
			@RequestParam(value = "pageSize", required = false) Integer pageSize,
			@RequestParam(value = "filter[logic]", required = false) String logic,
			@RequestParam(value = "filter[filters][0][value]", required = false) String value,
			@RequestParam(value = "filter[filters][0][field]", required = false) String fieldS,
			@RequestParam(value = "filter[filters][0][operator]", required = false) String operator,
			@RequestParam(value = "filter[filters][0][ignoreCase]", required = false) String ignorecase,
			@RequestParam(value = "criteria", required = false) String criteria,
			@RequestParam(value = "values", required = false) String values, HttpServletRequest request)
			throws SecurityException, ClassNotFoundException {
		String data = request.getQueryString();
		data = decode(data);
		String[] arrQueries = data.split("&");
		StringBuilder criteriaBuilder = new StringBuilder(criteria);
		StringBuilder valuesBuilder = new StringBuilder(values);
		for (String query : arrQueries) {
			if (query.contains("filter[filters][0][filter][field]")) {
				criteriaBuilder.append(",").append(query.replace("filter[filters][0][filter][field]=", ""));
			} else if (query.contains("filter[filters][0][filter][value]")) {
				valuesBuilder.append(",").append(query.replace("filter[filters][0][filter][value]=", ""));
			}
		}
		values = valuesBuilder.toString();
		criteria = criteriaBuilder.toString();
		if (field.equals("*")) {
			StringBuilder fieldBuilder = new StringBuilder();
			for (Field fieldItem : GetFields(Class.forName("com.jasamedika.medifirst2000.entities." + entity))) {
				String name = fieldItem.getName();
				if (name.equals("serialVersionsUID"))
					continue;
				boolean valid = false;
				for (Annotation annotation : fieldItem.getDeclaredAnnotations()) {
					if (annotation instanceof JoinColumn) {
						valid = true;
					} else if (annotation instanceof Column) {
						Column column = (Column) annotation;
						if (column.name().endsWith("Fk"))
							if (!fieldItem.getName().endsWith("Id"))
								valid = true;
					} else if (annotation instanceof OneToMany) {
						valid = true;
					}
				}
				if (!valid)
					if (fieldBuilder.toString().isEmpty())
						fieldBuilder.append(fieldItem.getName());
					else
						fieldBuilder.append(",").append(fieldItem.getName());
			}
			field = fieldBuilder.toString();
		} else if (field.equals("**")) {
			field = "*";
		}
		List<Map<String, Object>> modelGenericVO = modelService.getAllData(entity, field, take, skip, page, pageSize,
				logic, value, fieldS, operator, ignorecase, criteria, values);
		HttpHeaders headers = new HttpHeaders();
		headers.set(CONTENT_TYPE, APPLICATION_JSON_VALUE);
		try {
			return new ResponseEntity<>(ToMaps(modelGenericVO), headers, OK);
		} catch (IllegalArgumentException | IllegalAccessException e) {
			throw new ServiceVOException(e.getMessage());
		}
	}

	public static List<Field> GetFields(Class<?> data) {
		List<Field> items = new ArrayList<>();
		Class<?> parent = data.getSuperclass();
		if (parent != null)
			items.addAll(GetFields(data.getSuperclass()));
		items.addAll(Arrays.asList(data.getDeclaredFields()));
		return items;
	}

}
