package nc.service.rest.base;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.alibaba.fastjson.JSONObject;
import nc.bs.dao.BaseDAO;
import nc.bs.dao.DAOException;
import nc.bs.framework.adaptor.IHttpServletAdaptor;
import nc.bs.framework.common.InvocationInfoProxy;
import nc.bs.framework.common.NCLocator;
import nc.bs.framework.comn.NetStreamContext;
import nc.bs.framework.server.ISecurityTokenCallback;
import nc.bs.logging.Logger;
import nc.itf.uap.IUAPQueryBS;
import nc.jdbc.framework.processor.ColumnProcessor;
import nc.jdbc.framework.processor.MapListProcessor;
import nc.vo.pub.BusinessException;

/**
 * @Description: NC63对NCC系统提供的Rest接口公共基类
 * 
 * @date 2020-11-12
 * @version NCC2005
 */
@SuppressWarnings("restriction")
public abstract class NcToNccBaseRestService implements IHttpServletAdaptor, Serializable {

	private static final long serialVersionUID = 1L; 
	private BaseDAO dao = new BaseDAO();
	
	@Override
	public void doAction(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		
		JSONObject jsonObj = null; // NC系统推送过来的json字符串

		// 接口响应数据：目前响应数据比较简单，先用Map，后续有复杂数据结构时再封装成对象
		Map<String, Object> result = new HashMap<String, Object>();
		result.put("success", "true"); // 默认值：执行成功

		try {
			// 1.系统参数初始化
			initSystemParams("sysConfig.properties");
			
			// 2.获取报文数据
			jsonObj = getPostJsonValue(request);// 获取Post请求的值
			SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
			String beginDateStr = format.format(new Date()); // 请求处理开始时间
			Logger.info("请求时间：" + beginDateStr + "，Json报文:" + jsonObj);

			// 3.EsbKey验证
			if (!checkEsbkey(request)) { // 验证失败
				String errorMsg = "EsbKey授权验证失败!请检查EsbKey[" + request.getHeader("esbkey") + "]是否正确！";
				result.put("success", "false");
				result.put("message", errorMsg);
				businessErrorLog(request, jsonObj, errorMsg); // 业务日志记录（自定义）
				sendResponse(response, result); // 响应客户端
				return;
			}

			// 4.请求对应的业务处理
			if ((null == jsonObj) || (jsonObj.toString().trim().length() == 0)) { // 无Post提交值
				String errorMsg = "Post所提交请求Json格式错误或者无值!";
				result.put("success", "false");
				result.put("message", errorMsg);
				businessErrorLog(request, jsonObj, errorMsg); // 业务日志记录（自定义）
			} else {
				Logger.info(this.getClass().getName() + "接口执行正常");
				exeBoforeLog(request, jsonObj); // 接口执行前日志记录
				Object obj = execute(jsonObj); // 业务方法执行
				result.put("data", obj);
				exeAfterLog(request, jsonObj); // 接口执行后日志记录（自定义）
				Logger.info(this.getClass().getName() + "接口执行结束");
			}

		} catch (Exception e) {
			Logger.error("接口执行异常: " + e.getMessage());
			result.put("success", "false");
			result.put("message", "接口执行异常:" + e.getMessage());
			try {
				businessErrorLog(request, jsonObj, "接口执行异常:" + e.getMessage()); // 记录日志
			} catch (Exception e1) {
				Logger.error("记录日志失败 " + e1.getMessage());
			}
		}
		sendResponse(response, result); // 响应客户端
	}

	/**
	 * 初始化系统参数
	 * @throws IOException
	 * @throws BusinessException 
	 */
	public void initSystemParams(String configFileName) throws IOException, BusinessException {
		
		// 读取配置文件
		InputStream in = NcToNccBaseRestService.class.getResourceAsStream(configFileName);
		Properties props = new Properties();
		props.load(new InputStreamReader(in));
		
		InvocationInfoProxy proxy = InvocationInfoProxy.getInstance();
		proxy.setUserDataSource(props.getProperty("datasource")); // 数据源
		proxy.setGroupId(props.getProperty("group_id"));
		proxy.setUserId(props.getProperty("user_id"));
		proxy.setUserCode(props.getProperty("user_code"));
		
		String groupSql = "SELECT pk_group FROM org_group where nvl(dr,0)=0"; 
		String pk_group = (String) this.dao.executeQuery(groupSql,new ColumnProcessor());
		proxy.setGroupId(pk_group);
		
		ISecurityTokenCallback tc = NCLocator.getInstance().lookup(ISecurityTokenCallback.class);
		String tokenParam1 = props.getProperty("token_param1");
		String tokenParam2 = props.getProperty("token_param2");
		byte[] token = tc.token(tokenParam1.getBytes(), tokenParam2.getBytes());
		NetStreamContext.setToken(token);
	}
	
	/**
	 * 验证 EsbKey
	 * @param request 
	 * @return true(验证通过) || false(验证失败)
	 */
	@SuppressWarnings("unchecked")
	private boolean checkEsbkey(HttpServletRequest request) throws BusinessException {
		String interfaceName = request.getPathInfo(); // 请求路径
		String esbKey = request.getHeader("esbkey") == null ? "~" : request.getHeader("esbkey");
		if (interfaceName == null) {
			return false;
		}
		String sql = "select e.isstart,c.esbkey from bgy_esbinfo e left join bgy_certificate c on e.certificate = c.pk_code where "
				+ " e.interfacename = '" + interfaceName + "'";
		List<Map<String, String>> res = (List<Map<String, String>>) this.dao.executeQuery(sql, new MapListProcessor());
		if (res == null || res.size() != 1) {
			throw new BusinessException("接口安全配置信息错误");
		}
		String isStart = res.get(0).get("isstart");
		if (isStart == null || (!"Y".equals(isStart.trim()) && !"1".equals(isStart.trim()))) {
			throw new BusinessException("接口安全配置未启用");
		}
		String dbEsbKey = res.get(0).get("esbkey"); // 数据库中的EsbKey
		if (dbEsbKey != null && !"".equals(dbEsbKey.trim()) && !esbKey.equals(dbEsbKey.trim())) {
			throw new BusinessException("接口验证失败，请检查EsbKey是否正确");
		}
		return true;
	}
	
	/**
	 * 获取Post提交上来的Json参数
	 * 
	 * @param request
	 * @return
	 * @throws IOException 
	 * @throws Exception
	 */
	private JSONObject getPostJsonValue(HttpServletRequest request)
			throws BusinessException, IOException {
		BufferedReader br = null;
		try {
			br = new BufferedReader(new InputStreamReader((ServletInputStream) request.getInputStream(), "UTF-8"));
			StringBuffer sb = new StringBuffer();
			String temp = null;
			while ((temp = br.readLine()) != null) {
				sb.append(temp);
			}
			if (sb.length() == 0) {
				return new JSONObject();
			}
			return JSONObject.parseObject(sb.toString());

		} catch (Exception e) {
			throw new BusinessException("解析报文错误");

		} finally {
			if (br != null) {
				br.close();
			}
		}
	}
	
	/**
	 * 发送响应信息
	 * @param response
	 * @param esb
	 */
	public <T> void sendResponse(HttpServletResponse response, T respdata) {
		String jsonData = JSONObject.toJSONString(respdata);
		Logger.info("接口响应报文：" +jsonData);
		PrintWriter out = null;
		try {
			response.setHeader("Content-type", "application/json;charset=UTF-8");
			out = new PrintWriter(new OutputStreamWriter(response.getOutputStream(), "UTF-8"));
			out.write(jsonData);
			out.flush();
		} catch (IOException e) {
			Logger.error(this.getClass().getName() + "接口响应请求过程出错：" + e.getMessage());
		} finally {
			if (out != null) {
				out.close();
			}
		}
	}
	
	/**
	 * 接口执行前日志（自定义）
	 * 接口中需要自定义的处理业务日志时，子类重写此方法处理。
	 * @param request
	 * @param json
	 * @throws Exception
	 */
	protected void exeBoforeLog(HttpServletRequest request, JSONObject json) throws Exception { }
	
	/**
	 * 业务逻辑执行函数
	 * @param json
	 * @return
	 * @throws Exception
	 */
	protected abstract Object execute(JSONObject json) throws Exception;
	
	/**
	 * 接口执行成功后日志（自定义）
	 * 接口中需要自定义的处理业务日志时，子类重写此方法处理。
	 * @param request
	 * @param json
	 * @throws Exception
	 */
	protected void exeAfterLog(HttpServletRequest request, JSONObject json) throws Exception { }
	
	/**
	 * 业务错误日志记录（自定义）
	 * 接口业务错误日志需要自定义处理的话，子类重写此方法处理。
	 * @param request
	 * @param json
	 * @param errorMsg
	 * @throws Exceptioin
	 */
	protected void businessErrorLog(HttpServletRequest request, JSONObject json, String errorMsg) throws Exception { }
	
}
