package cz.muni.fi.lasaris.sbms.semantics.api;

import java.net.URI;

import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

import cz.muni.fi.lasaris.sbms.semantics.api.entities.Address;
import cz.muni.fi.lasaris.sbms.semantics.api.entities.Algorithm;
import cz.muni.fi.lasaris.sbms.semantics.api.entities.Influence;
import cz.muni.fi.lasaris.sbms.semantics.api.entities.ObservedProperty;
import cz.muni.fi.lasaris.sbms.semantics.api.entities.Scope;
import cz.muni.fi.lasaris.sbms.semantics.api.request.AddressRequest;
import cz.muni.fi.lasaris.sbms.semantics.api.request.AlgorithmRequest;
import cz.muni.fi.lasaris.sbms.semantics.api.request.Insert;
import cz.muni.fi.lasaris.sbms.semantics.api.response.AddressResponse;
import cz.muni.fi.lasaris.sbms.semantics.api.response.InfluenceResponse;
import cz.muni.fi.lasaris.sbms.semantics.logic.Fields;
import cz.muni.fi.lasaris.sbms.semantics.logic.ModelUpdater;
import cz.muni.fi.lasaris.sbms.semantics.logic.QueryParser;

@Path("relations")
@PermitAll
public class RelationsEndpoint {
	final static Logger logger = Logger.getLogger(RelationsEndpoint.class);
	
	@GET
	@Path("/influencing")
	@Produces(MediaType.APPLICATION_JSON)
	public AddressResponse getInfluencing(
		@QueryParam("influenced.scope.bimId") String scopeBimIdP,
		@QueryParam("influenced.property.domain") String propDomP,
		@QueryParam("influenced.property.quality") String propQualityP
		) {
		
		if(scopeBimIdP == null && propDomP == null && propQualityP == null) {
			return AddressResponse.getErrorResponse("Parameters must be set");
		}
		
		if(!(propDomP == null && propQualityP == null) 
				&& (propDomP == null || propQualityP == null)) {
			return AddressResponse.getErrorResponse("Both parameters (domain, quality) must be set");
		}
		
		if(propDomP != null && scopeBimIdP == null) {
			return AddressResponse.getErrorResponse("Scope must be set when property is set");
		}
		
		AddressRequest r = new AddressRequest();
	
		Address a = new Address();
		r.setAddress(a);
		
		Influence i = new Influence();
		if(StringUtils.isNotBlank(scopeBimIdP)) {
			i.setScope(new Scope());
			i.getScope().setBimId(scopeBimIdP);
		}
		if(StringUtils.isNotBlank(propDomP)) {
			i.setProperty(new ObservedProperty());
			i.getProperty().setDomain(propDomP);
		}
		if(StringUtils.isNotBlank(propQualityP)){
			if(i.getProperty() == null) {
				i.setProperty(new ObservedProperty());
			}
			i.getProperty().setQuality(propQualityP);
		}
		a.setInfluenced(i);
		r.getResponseFields().addAll(Fields.INFLUENCE_DATA_FIELDS);
		return QueryParser.getInfluencingResponse(r);
	}
	
	@GET
	@Path("/influenced")
	@Produces(MediaType.APPLICATION_JSON)
	public InfluenceResponse getInfluenced(
			@QueryParam("bmsId") String bmsIdP) {
		
		if(StringUtils.isBlank(bmsIdP)) {
			return InfluenceResponse.getErrorResponse("bmsId must be set");
		}
		AddressRequest r = new AddressRequest(new Address(bmsIdP));
		r.getResponseFields().addAll(Fields.INFLUENCE_DATA_FIELDS);
		return QueryParser.getInfluencedResponse(r);
	}
	
	@GET
	@Path("/used")
	@Produces(MediaType.APPLICATION_JSON)
	public AddressResponse getUsed(
			@QueryParam("bmsId") String bmsIdP) {
		
		if(StringUtils.isBlank(bmsIdP)) {
			return AddressResponse.getErrorResponse("bmsId must be set");
		}
		AlgorithmRequest r = new AlgorithmRequest(new Algorithm(bmsIdP));
		r.getResponseFields().addAll(Fields.USED_FIELDS);
		return QueryParser.getUsedResponse(r);
	}
	
	@GET
	@Path("/using")
	@Produces(MediaType.APPLICATION_JSON)
	public AddressResponse getUsing(
			@QueryParam("bmsId") String bmsIdP) {
		
		if(StringUtils.isBlank(bmsIdP)) {
			return AddressResponse.getErrorResponse("bmsId must be set");
		}
		AlgorithmRequest r = new AlgorithmRequest(new Algorithm());
		r.getAlgorithm().setUsedAddress(new Address(bmsIdP));
		r.getResponseFields().addAll(Fields.USED_FIELDS);
		return QueryParser.getUsingResponse(r);
	}
	
	@Context
	private UriInfo uriInfo;
	
	@RolesAllowed("admin")
	@POST
	@Consumes(MediaType.APPLICATION_JSON)
	public Response InsertAddressInfo(Insert insert) {
		try {
			if(insert.getAddress() != null && ModelUpdater.insertAddress(insert)) {
				URI createdUri = new URI(uriInfo.getAbsolutePath().toString() + insert.getAddress().getResourceId());
				return Response.created(createdUri).build();
			} else if (insert.getAlgorithm() != null && ModelUpdater.insertAlgorithm(insert)) {
				URI createdUri = new URI(uriInfo.getAbsolutePath().toString() + insert.getAlgorithm().getResourceId());
				return Response.created(createdUri).build();
			}
			else {
				return Response.status(Response.Status.BAD_REQUEST).entity("Invalid JSON data - see server log").build();
			}
		} catch (Exception e) {
			logger.error("Unable to insert address information", e);
			return Response.serverError().build();
		}
	}
	
	@RolesAllowed("admin")
	@DELETE
	@Path("/{bmsId}")
	@Consumes(MediaType.APPLICATION_JSON)
	public Response RemoveRelationInfo(@PathParam("bmsId") String bmsId) {
		System.out.println(bmsId);
		try {
			if(ModelUpdater.removeRelationAssertions(bmsId)) {
				return Response.ok().build();
			} else {
				return Response.status(Response.Status.BAD_REQUEST).entity("Invalid JSON data - see server log").build();
			}
		} catch (Exception e) {
			logger.error("Unable to remove relation", e);
			return Response.serverError().build();
		}
	}
}
