package in.cdac.medinfo.csnotk.csnoserv.rest;


import java.util.Arrays;
import java.util.Set;
import static  in.cdac.medinfo.csnotk.csnoserv.CSnoServResource.*;
import java.util.List;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import in.cdac.medinfo.csnotk.csnolib.commons.EnumAcceptability;
import in.cdac.medinfo.csnotk.csnolib.commons.EnumDirection;
import in.cdac.medinfo.csnotk.csnolib.commons.EnumRelationshipType;
import in.cdac.medinfo.csnotk.csnolib.commons.EnumState;
import in.cdac.medinfo.csnotk.csnolib.model.CompositeDescription;
import in.cdac.medinfo.csnotk.csnolib.model.Concept;
import in.cdac.medinfo.csnotk.csnolib.model.Description;
import in.cdac.medinfo.csnotk.csnoserv.dto.ValidatorDTO;



@Path("/validate")
public class ValidateController{
	
	public static final String THECONCEPT="The concept ";
	

    /**
     * Validates the given SCT ID and return with a validation message
     * @param id  {@link String} concept id for which validation is to be done
     * @param moduleId {@link String} validate the given id within the given module-id
     * @param parentid {@link String} validate whether given concept id is a child of the given parent id(s)
     * **/
    @GET
    @Produces({ MediaType.APPLICATION_JSON + ";charset=utf-8" })
    @Consumes({ MediaType.APPLICATION_FORM_URLENCODED })
    @Path("/id")
    public Response validateSctId(@QueryParam("id") String id,
                                  @QueryParam("parentid") @DefaultValue("") String parentId,
                                  @QueryParam("moduleId") @DefaultValue("") String moduleId) {
        boolean isParent = true;
        StringBuilder finalMessage = new StringBuilder();
        initProperties();
        //Response Object
        ValidatorDTO dto = new ValidatorDTO();
        try {
            Long.parseLong(id);
        } catch (NumberFormatException exception) {
            dto.setValueBoolean(false);
            dto.setValueString(THECONCEPT + id + " is not valid");
            return CORSHeaderSupport.addCORSHeader(Response.Status.OK, gson.toJson(dto));
        }
        Set<CompositeDescription> compositDescription = agent.search(id, EnumState.getEnum("active"),
                Arrays.asList("all"), EnumAcceptability.getEnum("all"), -1, false, null, null,
                Boolean.parseBoolean("true"), false);

        if (compositDescription.isEmpty()) {
            dto.setValueBoolean(false);
            dto.setValueString(THECONCEPT + id + " is not valid");
        } else {
            finalMessage.append(THECONCEPT + id + " is valid");
            // checks if result is to be searched with parent id
            if(!(parentId.equals(""))) {
                isParent = validateParentIdList(id, parentId, finalMessage);
            }
            // checks if result is to be searched with module id
            if((moduleId.equals(""))) {
                dto.setValueBoolean(isParent);
            } else {
                boolean inModule = this.validateModuleId(moduleId, compositDescription);
                if(inModule)
                    finalMessage.append(BELONGS_TO + " module "+moduleId);
                else
                    finalMessage.append(DOES_NOT_BELONG_TO + " module "+moduleId);
                //Final Response
                dto.setValueBoolean(isParent && inModule);
            }
            dto.setValueString(finalMessage.toString());
        }
        return CORSHeaderSupport.addCORSHeader(Response.Status.OK, gson.toJson(dto));

    }

    /**
     * validate the given module id present in index
     *
     * @param {@link CompositeDescription} is extended from {@link Description}}
     * @return {@link boolean}
     **/
    private boolean validateModuleId(String moduleId, Set<CompositeDescription> descriptions) {
        for (CompositeDescription description : descriptions) {
            if (description.getModuleId().equals(moduleId))
                return true;
        }
        return false;
    }



    /**
     * Validates whether given concept has the given parent
     *
     * @param conceptId {@link String} concept id
     * @param parentId {@link String} parent id (to verify)
     * @return {@link boolean}
     **/
    private boolean validateParentId(String conceptId, String parentId) {
        Set<Concept> list = agent.getRelations(conceptId.trim(), EnumRelationshipType.IS_A.getValue(), EnumDirection.SOURCE);
        for (Concept concept : list) {
            if (concept.getId().equals(parentId))
                return true;
        }
        return false;
    }

    /**
     * Validates whether given concept is a child of any of the parents.
     * Helper for validateSctId method.
     *
     * @param conceptId {@link String} concept id
     * @param parentString {@link String} parent ids (to verify)
     * @param message {@link StringBuilder} final message
     * @return {@link boolean}
     **/
    private boolean validateParentIdList(String conceptId, String parentString, StringBuilder message) {
        boolean isParent = false;
        String validParentId = "";
        //Collect parent IDs
        List<String> parentIdList = Arrays.asList(parentString.split("\\s{2}"));
        //Checking parent(s) validity
        for(String parent : parentIdList) {
            isParent = validateParentId(conceptId, parent);
            if(isParent) {
                validParentId = parent;
                break;
            }
        }
        if(isParent)
            message.append(BELONGS_TO + " parent " + validParentId);
        else
            message.append(DOES_NOT_BELONG_TO + " parent " + parentIdList.toString());
        return isParent;
    }

}
