/**********************************************************************
* This file is part of iDempiere ERP Open Source                      *
* http://www.idempiere.org                                            *
*                                                                     *
* Copyright (C) Contributors                                          *
*                                                                     *
* This program is free software; you can redistribute it and/or       *
* modify it under the terms of the GNU General Public License         *
* as published by the Free Software Foundation; either version 2      *
* of the License, or (at your option) any later version.              *
*                                                                     *
* This program is distributed in the hope that it will be useful,     *
* but WITHOUT ANY WARRANTY; without even the implied warranty of      *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the        *
* GNU General Public License for more details.                        *
*                                                                     *
* You should have received a copy of the GNU General Public License   *
* along with this program; if not, write to the Free Software         *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,          *
* MA 02110-1301, USA.                                                 *
*                                                                     *
* Contributors:                                                       *
* - Trek Global Corporation                                           *
* - Heng Sin Low                                                      *
**********************************************************************/
package com.trekglobal.idempiere.rest.api.v1.resource.impl;

import java.util.List;
import java.util.logging.Level;

import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

import org.compiere.model.MColumn;
import org.compiere.model.MTable;
import org.compiere.model.Query;
import org.compiere.util.CLogger;
import org.compiere.util.Env;
import org.compiere.util.Util;

import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.trekglobal.idempiere.rest.api.json.ResponseUtils;
import com.trekglobal.idempiere.rest.api.json.filter.ConvertedQuery;
import com.trekglobal.idempiere.rest.api.json.filter.IQueryConverter;
import com.trekglobal.idempiere.rest.api.model.MRestView;
import com.trekglobal.idempiere.rest.api.model.MRestViewColumn;
import com.trekglobal.idempiere.rest.api.model.MRestViewRelated;
import com.trekglobal.idempiere.rest.api.v1.resource.ViewResource;

public class ViewResourceImpl implements ViewResource {

	private final static CLogger log = CLogger.getCLogger(ViewResourceImpl.class);
	
	public ViewResourceImpl() {
	}

	private ModelResourceImpl restView() {
		return new ModelResourceImpl().restView();
	}
	
	public Response getPO(String tableName, String id, String details, String select, String showsql) {
		return getPO(tableName, id, details, select, showsql, null);
	}
	
	@Override
	public Response getPO(String tableName, String id, String details, String select, String showsql, String showlabel) {
		return restView().getPO(tableName, id, details, select, showsql, showlabel);
	}

	@Override
	public Response getPOProperty(String tableName, String id, String propertyName, String showsql) {
		return restView().getPOProperty(tableName, id, propertyName, showsql);
	}

	public Response getPOs(String tableName, String details, String filter, String order, String select, int top,
			int skip, String validationRuleID, String context, String showsql) {
		return getPOs(tableName, details, filter, order, select, top, skip, validationRuleID, context, showsql, null, null);
	}
	
	@Override
	public Response getPOs(String tableName, String details, String filter, String order, String select, int top,
			int skip, String validationRuleID, String context, String showsql, String label, String showlabel) {
		return restView().getPOs(tableName, details, filter, order, select, top, skip, validationRuleID, context, showsql, 
				label, showlabel);
	}

	@Override
	public Response getModels(String filter) {
		IQueryConverter converter = IQueryConverter.getQueryConverter("DEFAULT");
		try {
			ConvertedQuery convertedStatement = converter.convertStatement(MRestView.Table_Name, filter);

			if (log.isLoggable(Level.INFO)) log.info("Where Clause: " + convertedStatement.getWhereClause());

			Query query = new Query(Env.getCtx(), MRestView.Table_Name, convertedStatement.getWhereClause(), null);
			query.setOnlyActiveRecords(true).setApplyAccessFilter(true);
			query.setParameters(convertedStatement.getParameters());

			List<MRestView> views = query.setOrderBy("REST_View.Name").list();
			JsonArray array = new JsonArray();
			for(MRestView view : views) {
				JsonObject json = new JsonObject();
				json.addProperty("id", view.getREST_View_ID());
				if (!Util.isEmpty(view.getREST_View_UU())) {
					json.addProperty("uid", view.getREST_View_UU());
				}
				json.addProperty("name", view.getName());
				MTable table = MTable.get(view.getAD_Table_ID());
				json.addProperty("tableName", table.getTableName().toLowerCase());
				
				if (!Util.isEmpty(view.getWhereClause())) {
					json.addProperty("whereClause", view.getWhereClause());
				}
				MRestViewColumn[] columns = view.getColumns();
				if (columns.length > 0) {
					JsonArray columnArray = new JsonArray();
					for(MRestViewColumn column : columns) {
						JsonObject columnJson = new JsonObject();
						columnJson.addProperty("id", column.get_ID());
						if (!Util.isEmpty(column.getREST_ViewColumn_UU())) {
							columnJson.addProperty("uid", column.getREST_ViewColumn_UU());
						}
						columnJson.addProperty("name", column.getName());
						MColumn tableColumn = MColumn.get(column.getAD_Column_ID());								
						columnJson.addProperty("columnName", tableColumn.getColumnName());
						String helpText = null;
						if (!Util.isEmpty(tableColumn.get_Translation(MColumn.COLUMNNAME_Help)))
							helpText = tableColumn.get_Translation(MColumn.COLUMNNAME_Help);
						else
							helpText = tableColumn.get_Translation(MColumn.COLUMNNAME_Description);
						if (!Util.isEmpty(helpText))
							columnJson.addProperty("help", helpText);
						if (column.getREST_ReferenceView_ID() > 0) {
							MRestView referenceView = MRestView.get(column.getREST_ReferenceView_ID());
							JsonObject refJson = new JsonObject();
							refJson.addProperty("id", referenceView.get_ID());
							if (!Util.isEmpty(referenceView.getREST_View_UU()))
								refJson.addProperty("uid", referenceView.getREST_View_UU());
							refJson.addProperty("name", referenceView.getName());
							columnJson.add("referenceView", refJson);
						}
						columnArray.add(columnJson);
					}
					json.add("columns", columnArray);
				}
				MRestViewRelated[] relateds = view.getRelatedViews();
				if (relateds.length > 0) {
					JsonArray relatedArray = new JsonArray();
					for(MRestViewRelated related : relateds) {
						JsonObject relatedJson = new JsonObject();
						relatedJson.addProperty("id", related.get_ID());
						if (!Util.isEmpty(related.getREST_ViewRelated_UU())) {
							relatedJson.addProperty("uid", related.getREST_ViewRelated_UU());
						}
						relatedJson.addProperty("name", related.getName());
						MRestView referenceView = MRestView.get(related.getREST_RelatedRestView_ID());
						JsonObject refJson = new JsonObject();
						refJson.addProperty("id", referenceView.get_ID());
						if (!Util.isEmpty(referenceView.getREST_View_UU()))
							refJson.addProperty("uid", referenceView.getREST_View_UU());
						refJson.addProperty("name", referenceView.getName());
						relatedJson.add("view", refJson);
						relatedJson.addProperty("autoExpand", related.isRestAutoExpand());
						relatedArray.add(relatedJson);
					}
					json.add("relatedViews", relatedArray);
				}
				array.add(json);
			}
			JsonObject json = new JsonObject();
			json.add("views", array);
			return Response.ok(json.toString()).build();			
		} catch (Exception ex) {
			return ResponseUtils.getResponseErrorFromException(ex, "GET Error");
		}	
	}

	@Override
	public Response create(String tableName, String jsonText) {
		return restView().create(tableName, jsonText);
	}

	@Override
	public Response update(String tableName, String id, String jsonText) {
		return restView().update(tableName, id, jsonText);
	}

	@Override
	public Response delete(String tableName, String id) {
		return restView().delete(tableName, id);
	}

	@Override
	public Response getAttachments(String tableName, String id) {
		return restView().getAttachments(tableName, id);
	}

	@Override
	public Response getAttachmentsAsZip(String tableName, String id, String asJson) {
		return restView().getAttachmentsAsZip(tableName, id, asJson);
	}

	@Override
	public Response createAttachmentsFromZip(String tableName, String id, String jsonText) {
		return restView().createAttachmentsFromZip(tableName, id, jsonText);
	}

	@Override
	public Response getAttachmentEntry(String tableName, String id, String fileName, String asJson) {
		return restView().getAttachmentEntry(tableName, id, fileName, asJson);
	}

	@Override
	public Response addAttachmentEntry(String tableName, String id, String jsonText) {
		return restView().addAttachmentEntry(tableName, id, jsonText);
	}

	@Override
	public Response deleteAttachments(String tableName, String id) {
		return restView().deleteAttachments(tableName, id);
	}

	@Override
	public Response deleteAttachmentEntry(String tableName, String id, String fileName) {
		return restView().deleteAttachmentEntry(tableName, id, fileName);
	}

	@Override
	public Response printModelRecord(String tableName, String id, String reportType) {
		return restView().printModelRecord(tableName, id, reportType);
	}

	@Override
	public Response getModelYAML(String tableName) {
		MRestView view = MRestView.get(tableName);
		if (view == null)
			return ResponseUtils.getResponseError(Status.NOT_FOUND, "Invalid view name", "No match found for view name: ", tableName);
		
		StringBuilder header = new StringBuilder();
		header.append("openapi: 3.0.0\n");
		header.append("info:\n");
		header.append(" ".repeat(2)).append("title: views/").append(tableName).append("\n");
		header.append(" ".repeat(2)).append("version: 1.0.0\n");
		YAMLSchema.addServers(header);
		header.append("components:\n");
		YAMLSchema.addSecuritySchema(header);
		YAMLSchema.addPredefinedParameters(header);
		header.append(" ".repeat(2)).append("schemas:\n");
		
		StringBuilder body = new StringBuilder();		
		buildYAMLForView(view, body);
		
		YAMLSchema.addErrorResponseReference(header);
		if (body.indexOf("#/components/schemas/Image") > 0) {
			YAMLSchema.addImageReference(header);
		}
		if (body.indexOf("#/components/schemas/Location") > 0) {
			YAMLSchema.addLocationReference(header, 4);			
		}
		
		addRelatedViews(view, header);
		
		YAMLSchema.addSecurityHeader(body);
		
		body.append("paths:\n");
		YAMLSchema.addAuthRequest(body);
		YAMLSchema.addModelRequest(tableName, true, body);
						
		return Response.status(Status.OK).entity(header.append(body.toString()).toString()).build();
	}

	private void addRelatedViews(MRestView view, StringBuilder header) {
		MRestViewRelated[] relatedViews = view.getRelatedViews();
		for (MRestViewRelated relatedView : relatedViews) {
			//check tree, avoid infinite loop
			if (view.getREST_View_ID() == relatedView.getREST_RelatedRestView_ID())
				continue;
			MRestView childView = MRestView.get(relatedView.getREST_RelatedRestView_ID()); 
			buildYAMLForView(childView, header);				
			addRelatedViews(childView, header);
		}
	}
	
	private void buildYAMLForView(MRestView view, StringBuilder body) {
		MTable table = MTable.get(view.getAD_Table_ID());
		
		body.append(" ".repeat(4)).append(view.getName()).append(":\n");
		body.append(" ".repeat(6)).append("type: object\n");
		body.append(" ".repeat(6)).append("properties:\n");
		if (table.getKeyColumns() != null && table.getKeyColumns().length == 1 && table.getKeyColumns()[0].endsWith("_ID")) {
			body.append(" ".repeat(8)).append("id:\n");
			body.append(" ".repeat(10)).append("type: integer\n");
			body.append(" ".repeat(10)).append("description: record id\n");
			body.append(" ".repeat(10)).append("readOnly: true\n");
		}
		body.append(" ".repeat(8)).append("uid:\n");
		body.append(" ".repeat(10)).append("type: string\n");
		body.append(" ".repeat(10)).append("description: record uuid\n");
		body.append(" ".repeat(10)).append("readOnly: true\n");
		
		List<String> valueObjectNames = YAMLSchema.addViewProperties(view, body, 8);
		//nested json value object
		if (valueObjectNames.size() > 0) {
			for(String valueObjectName : valueObjectNames) {
				body.append(" ".repeat(4)).append(view.getName()).append("_").append(valueObjectName).append(":\n");
				body.append(" ".repeat(6)).append("type: object\n");
				body.append(" ".repeat(6)).append("properties:\n");
				YAMLSchema.addValueObjectProperties(view, valueObjectName, body, 8);				
			}
		}
	}	
}
