/*
 * Copyright 2015 Ranjan Kumar
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.restfiddle.controller.rest;

import java.util.Date;
import java.util.List;

import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.restfiddle.dao.GenericEntityDataRepository;
import com.restfiddle.dao.GenericEntityRepository;
import com.restfiddle.dao.NodeRepository;
import com.restfiddle.dto.ConversationDTO;
import com.restfiddle.dto.NodeDTO;
import com.restfiddle.dto.RfRequestDTO;
import com.restfiddle.dto.StatusResponse;
import com.restfiddle.entity.BaseNode;
import com.restfiddle.entity.GenericEntity;
import com.restfiddle.entity.GenericEntityField;

@RestController
@Transactional
public class GenerateApiController {
    private static final String ENTITIES = "/entities/";
    private static final String API = "/api/";

    Logger logger = LoggerFactory.getLogger(GenerateApiController.class);

    @Value("${application.host-uri}")
    private String hostUri;

    @Autowired
    private NodeController nodeController;

    @Autowired
    private NodeRepository nodeRepository;

    @Autowired
    private ConversationController conversationController;

    @Autowired
    private GenericEntityRepository genericEntityRepository;

    @Autowired
    private GenericEntityDataRepository genericEntityDataRepository;

    @RequestMapping(value = "/api/entities/{id}/generate-api", method = RequestMethod.POST)
    public @ResponseBody
    StatusResponse generateApiByEntityId(@PathVariable("id") String id) {
	logger.debug("Generating APIs for entity with id: " + id);

	GenericEntity entity = genericEntityRepository.findOne(id);

	String baseNodeId = entity.getBaseNodeId();
	BaseNode entityNode = nodeController.findById(baseNodeId);

	return generateApi(entityNode);
    }
    
    StatusResponse generateApiByEntity(GenericEntity entity) {
	
	String baseNodeId = entity.getBaseNodeId();
	BaseNode entityNode = nodeController.findById(baseNodeId);
	
	return generateApi(entityNode);
    }

    public StatusResponse generateApi(BaseNode entityNode) {
	GenericEntity genericEntity = entityNode.getGenericEntity();

	// API to GENERATE >> List of Entity Data
	ConversationDTO conversationDTO = new ConversationDTO();
	RfRequestDTO rfRequestDTO = new RfRequestDTO();

	String projectId = entityNode.getProjectId();

	rfRequestDTO.setApiUrl(hostUri + API + projectId + ENTITIES + entityNode.getName() + "/list");
	rfRequestDTO.setMethodType("GET");
	conversationDTO.setRfRequestDTO(rfRequestDTO);

	ConversationDTO createdConversation = conversationController.create(conversationDTO);
	conversationDTO.setId(createdConversation.getId());

	String nodeName = "Get List of " + entityNode.getName();
	createNode(nodeName, entityNode.getId(), projectId, conversationDTO);

	// API to GENERATE >> Get Entity Data By Id
	conversationDTO = new ConversationDTO();
	rfRequestDTO = new RfRequestDTO();
	rfRequestDTO.setApiUrl(hostUri + API + projectId + ENTITIES + entityNode.getName() + "/{uuid}");
	rfRequestDTO.setMethodType("GET");
	conversationDTO.setRfRequestDTO(rfRequestDTO);

	createdConversation = conversationController.create(conversationDTO);
	conversationDTO.setId(createdConversation.getId());

	nodeName = "Get " + entityNode.getName() + " by Id";
	createNode(nodeName, entityNode.getId(), projectId, conversationDTO);

	// API to GENERATE >> Delete Entity Data By Id
	conversationDTO = new ConversationDTO();
	rfRequestDTO = new RfRequestDTO();
	rfRequestDTO.setApiUrl(hostUri + API + projectId + ENTITIES + entityNode.getName() + "/{uuid}");
	rfRequestDTO.setMethodType("DELETE");
	conversationDTO.setRfRequestDTO(rfRequestDTO);

	createdConversation = conversationController.create(conversationDTO);
	conversationDTO.setId(createdConversation.getId());

	nodeName = "Delete " + entityNode.getName();
	createNode(nodeName, entityNode.getId(), projectId, conversationDTO);

	// API to GENERATE >> Create Entity Data
	conversationDTO = new ConversationDTO();
	rfRequestDTO = new RfRequestDTO();
	rfRequestDTO.setApiUrl(hostUri + API + projectId + ENTITIES + entityNode.getName());
	rfRequestDTO.setMethodType("POST");

	JSONObject jsonObject = getFieldJson(genericEntity);
	// Make a pretty-printed JSON text of this JSONObject.
	rfRequestDTO.setApiBody(jsonObject.toString(4));
	conversationDTO.setRfRequestDTO(rfRequestDTO);

	createdConversation = conversationController.create(conversationDTO);
	conversationDTO.setId(createdConversation.getId());

	nodeName = "Create " + entityNode.getName();
	createNode(nodeName, entityNode.getId(), projectId, conversationDTO);

	// API to GENERATE >> Update Entity Data
	conversationDTO = new ConversationDTO();
	rfRequestDTO = new RfRequestDTO();
	rfRequestDTO.setApiUrl(hostUri + API + projectId + ENTITIES + entityNode.getName() + "/{uuid}");
	rfRequestDTO.setMethodType("PUT");

	jsonObject = getFieldJson(genericEntity);
	// id is required in case of update.
	jsonObject.put("_id", "{uuid}");

	rfRequestDTO.setApiBody(jsonObject.toString(4));
	conversationDTO.setRfRequestDTO(rfRequestDTO);

	createdConversation = conversationController.create(conversationDTO);
	conversationDTO.setId(createdConversation.getId());

	nodeName = "Update " + entityNode.getName();
	createNode(nodeName, entityNode.getId(), projectId, conversationDTO);
	
	if(entityNode.getName().equals("User")){
	 // API to GENERATE >> Login Entity
	    conversationDTO = new ConversationDTO();
	    rfRequestDTO = new RfRequestDTO();
	    rfRequestDTO.setApiUrl(hostUri + API + projectId + "/entities/login");
	    rfRequestDTO.setMethodType("POST");

	    JSONObject json = new JSONObject();
	    json.put("username", "");
	    json.put("password", "");

	    rfRequestDTO.setApiBody(json.toString(4));
	    conversationDTO.setRfRequestDTO(rfRequestDTO);

	    createdConversation = conversationController.create(conversationDTO);
	    conversationDTO.setId(createdConversation.getId());

	    nodeName = "Login " + entityNode.getName();
	    createNode(nodeName, entityNode.getId(), projectId, conversationDTO);
	    
	 // API to GENERATE >> Get Entity Data By Id
	    conversationDTO = new ConversationDTO();
	    rfRequestDTO = new RfRequestDTO();
	    rfRequestDTO.setApiUrl(hostUri + API + projectId + "/entities/logout?authToken=");
	    rfRequestDTO.setMethodType("GET");
	    conversationDTO.setRfRequestDTO(rfRequestDTO);

	    createdConversation = conversationController.create(conversationDTO);
	    conversationDTO.setId(createdConversation.getId());

	    nodeName = "Logout " + entityNode.getName();
	    createNode(nodeName, entityNode.getId(), projectId, conversationDTO);
	}

	return null;
    }

    private NodeDTO createNode(String nodeName, String parentId, String projectId, ConversationDTO conversationDTO) {
	NodeDTO childNode = new NodeDTO();
	childNode.setName(nodeName);
	childNode.setProjectId(projectId);
	childNode.setConversationDTO(conversationDTO);
	NodeDTO createdNode = nodeController.create(parentId, childNode);
	return createdNode;
    }

    private JSONObject getFieldJson(GenericEntity genericEntity) {
	// Create JSON template for the entity data based on fields and set it as api body.
	List<GenericEntityField> fields = genericEntity.getFields();
	JSONObject jsonObject = new JSONObject();
	for (GenericEntityField genericEntityField : fields) {
	    String type = genericEntityField.getType();
	    if ("STRING".equalsIgnoreCase(type)) {
		jsonObject.put(genericEntityField.getName(), "");
	    } else if ("NUMBER".equalsIgnoreCase(type)) {
		jsonObject.put(genericEntityField.getName(), Long.valueOf(1));
	    } else if ("BOOLEAN".equalsIgnoreCase(type)) {
		jsonObject.put(genericEntityField.getName(), false);
	    } else if ("DATE".equalsIgnoreCase(type)) {
		jsonObject.put(genericEntityField.getName(), new Date());
	    } else if ("OBJECT".equalsIgnoreCase(type)) {
		jsonObject.put(genericEntityField.getName(), new JSONObject());
	    } else if ("ARRAY".equalsIgnoreCase(type)) {
		jsonObject.put(genericEntityField.getName(), new JSONArray());
	    } else if ("Geographic point".equalsIgnoreCase(type)) {
		jsonObject.put(genericEntityField.getName(), new JSONObject("{\"lat\" : 18.5204303,\"lng\" : 73.8567437}"));
	    } else if ("relation".equalsIgnoreCase(type)) {
		jsonObject.put(genericEntityField.getName(), new JSONObject("{\"_rel\":{\"entity\" : \"{Entity Name}\",\"_id\" : \"{Entity _id}\"}}"));
	    }
	}
	return jsonObject;
    }
}
