package org.gcube.application.geoportal.client;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.gcube.application.geoportal.client.utils.Serialization;
import org.gcube.application.geoportal.common.model.configuration.Configuration;
import org.gcube.application.geoportal.common.model.legacy.Concessione;
import org.gcube.application.geoportal.common.model.rest.AddSectionToConcessioneRequest;
import org.gcube.application.geoportal.common.model.rest.QueryRequest;
import org.gcube.application.geoportal.common.rest.InterfaceConstants;
import org.gcube.application.geoportal.common.rest.MongoConcessioni;
import org.gcube.common.clients.Call;
import org.gcube.common.clients.delegates.ProxyDelegate;

import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.io.InputStream;
import java.rmi.RemoteException;
import java.util.Iterator;

@RequiredArgsConstructor
@Slf4j
public class DefaultMongoConcessioni implements MongoConcessioni{

	@NonNull
	private final ProxyDelegate<WebTarget> delegate;
	
	private final ObjectMapper mapper=Serialization.mapper;
	
	@Override
	public Concessione createNew(Concessione c) throws Exception {
		log.debug("Serializing {} ",c);
		final String serialized=mapper.writeValueAsString(c);
		Call<WebTarget,Concessione> call= endpoint -> {
			Response resp= endpoint.request(MediaType.APPLICATION_JSON).post(Entity.entity(serialized, MediaType.APPLICATION_JSON));
			return check(resp,Concessione.class);
		};
		Concessione toReturn = delegate.make(call);
		log.info("Registered {}",toReturn);
		return toReturn;
	}

	@Override
	public void deleteById(String id) throws Exception {
	deleteById(id,false);
	}

	@Override
	public void deleteById(String id,Boolean force) throws Exception {
		log.debug("Deleting by id {}",id);
		delegate.make((Call<WebTarget, String>) endpoint -> {
			check(endpoint.path(id).queryParam(InterfaceConstants.Parameters.FORCE,force).request(MediaType.APPLICATION_JSON).delete(),null);
			log.info("Deleted by id {}",id);
			return null;
		});
	}

	@Override
	public Concessione getById(String id) throws Exception {
		log.debug("Loading by id {}",id);
		Call<WebTarget,Concessione> call= endpoint -> {
			Response resp= endpoint.path(id).request(MediaType.APPLICATION_JSON).get();
			return check(resp,Concessione.class);
		};
		Concessione toReturn = delegate.make(call);
		log.debug("Loaded {}",toReturn);
		return toReturn;
	}

	@Override
	public Iterator<Concessione> getList() throws Exception {
		log.debug("Getting list");
		Call<WebTarget,Iterator<Concessione>> call= endpoint -> mapper.readerFor(Concessione.class).readValues(
				endpoint.request(MediaType.APPLICATION_JSON).get(InputStream.class));
		return delegate.make(call);
	}

	@Override
	public Concessione publish(String id) throws Exception {
		log.info("Publishing {} ",id);
		Call<WebTarget,Concessione> call= endpoint -> {
			Response resp= endpoint.path(InterfaceConstants.Methods.PUBLISH_PATH).
					path(id).request(MediaType.APPLICATION_JSON).put(Entity.entity(id, MediaType.APPLICATION_JSON));
			return check(resp,Concessione.class);
		};
		Concessione toReturn = delegate.make(call);
		log.debug("Published {} ",toReturn);
		return toReturn;
	}

	@Override
	public void unPublish(String id) throws Exception {
		log.info("Publishing {} ",id);

		Call<WebTarget,Concessione> call= endpoint -> {
			Response resp= endpoint.path(InterfaceConstants.Methods.PUBLISH_PATH).
					path(id).request(MediaType.APPLICATION_JSON).delete();
			check(resp,null);
			return null;
		};
		delegate.make(call);
		log.debug("UnPublished {} ",id);
	}

	@Override
	public Configuration getCurrentConfiguration() throws Exception {
		return delegate.make(webTarget -> check(webTarget.path(InterfaceConstants.Methods.CONFIGURATION_PATH).
				request(MediaType.APPLICATION_JSON).get(),Configuration.class));
	}

	@Override
	public Iterator<Concessione> search(String s) throws Exception {
		log.debug("Searching for {}",s);
		Call<WebTarget,Iterator<Concessione>> call= endpoint -> {
			Response resp =endpoint.path(InterfaceConstants.Methods.SEARCH_PATH).request(MediaType.APPLICATION_JSON).
					post(Entity.entity(s,MediaType.APPLICATION_JSON));
			return checkCollection(resp,Concessione.class);
		};
		return delegate.make(call);
	}

	@Override
	public Iterator<Concessione> query(QueryRequest queryRequest) throws Exception {
		log.debug("Querying for {}",queryRequest);
		return queryForType(queryRequest,Concessione.class);
	}

	@Override
	public String queryForJSON(QueryRequest queryRequest) throws Exception {
		return null;
	}

	@Override
	public <T> Iterator<T> queryForType(QueryRequest queryRequest, Class<T> aClass) throws Exception {
		log.debug("Querying for {}",queryRequest);
		Call<WebTarget, Iterator<T>> call= endpoint -> {
			Response resp =endpoint.path(InterfaceConstants.Methods.QUERY_PATH).request(MediaType.APPLICATION_JSON).
					post(Entity.entity(queryRequest,MediaType.APPLICATION_JSON));
			return checkCollection(resp,aClass);
		};
		return delegate.make(call);
	}


	@Override
	public Concessione registerFileSet(String id, AddSectionToConcessioneRequest request) throws Exception {
		log.info("Registering {} in {}",request,id);
		request.validate();
		Call<WebTarget,Concessione> call= endpoint -> {
			Response resp= endpoint.path(InterfaceConstants.Methods.REGISTER_FILES_PATH).
					path(id).request(MediaType.APPLICATION_JSON).post(Entity.entity(mapper.writeValueAsString(request),
							MediaType.APPLICATION_JSON));
			return check(resp,Concessione.class);
		};
		Concessione toReturn = delegate.make(call);
		log.debug("Registered Fileset {} result is {} ",request,toReturn);
		return toReturn;
	}

	@Override
	public Concessione cleanFileSet(String id, String path)throws Exception{
		log.info("Cleaning Fileset at {} in {}",path,id);
		Call<WebTarget,Concessione> call= endpoint -> {
			Response resp= endpoint.path(InterfaceConstants.Methods.DELETE_FILES_PATH).
					path(id).request(MediaType.APPLICATION_JSON).post(Entity.entity(path,
					MediaType.APPLICATION_JSON));
			return check(resp,Concessione.class);
		};
		Concessione toReturn = delegate.make(call);
		log.debug("Cleaned path {} result {} ",path,toReturn);
		return toReturn;
	}

	@Override
	public Concessione update(String id, String jsonUpdate) throws Exception {
		log.info("Updating {} ",id);
		log.debug("JSON is {} ",jsonUpdate);
		Call<WebTarget,Concessione> call= endpoint -> {
			Response resp= endpoint.path(id).
					request(MediaType.APPLICATION_JSON).put(Entity.entity(jsonUpdate,
					MediaType.APPLICATION_JSON));
			return check(resp,Concessione.class);
		};
		Concessione toReturn = delegate.make(call);
		log.debug("Updated {} is {} ",id,toReturn);
		return toReturn;
	}

	@Override
	public Concessione replace(Concessione replacement) throws Exception {
		log.info("Replacing {}",replacement);
		Call<WebTarget,Concessione> call= endpoint -> {
			Response resp= endpoint.
					request(MediaType.APPLICATION_JSON).
					put(Entity.entity(mapper.writeValueAsString(replacement),
							MediaType.APPLICATION_JSON));
			return check(resp,Concessione.class);
		};
		Concessione toReturn = delegate.make(call);
		log.debug("Reloaded {} ",toReturn);
		return toReturn;
	}





	protected static<T> T check(Response resp, Class<T> clazz) throws IOException {
		String resString=resp.readEntity(String.class);
		if(resp.getStatus()<200||resp.getStatus()>=300)
			throw new RemoteException("RESP STATUS IS "+resp.getStatus()+". Message : "+resString);
		if(clazz!=null)
			return Serialization.read(resString, clazz);
		else return null;
	}

	protected static<T> Iterator<T> checkCollection(Response resp, Class<T> clazz) throws IOException {
		if(resp.getStatus()<200||resp.getStatus()>=300)
			throw new RemoteException("RESP STATUS IS "+resp.getStatus()+". Message : "+resp.readEntity(String.class));
		if(clazz!=null)
			return Serialization.readCollection((InputStream) resp.getEntity(), clazz);
		else return null;
	}


}
