package cn.hmsoft.pay.impl.weixin;

import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;
import java.util.TreeMap;

import javax.servlet.http.HttpServletResponse;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

import cn.hmsoft.frame.exception.BusinessException;
import cn.hmsoft.frame.util.FrameAssertUtil;
import cn.hmsoft.helper.BeanHelper;
import cn.hmsoft.helper.SecureHelper;
import cn.hmsoft.helper.StringHelper;
import cn.hmsoft.helper.qrcode.QrCodeEntity;
import cn.hmsoft.helper.qrcode.QrCodeHelper;
import cn.hmsoft.pay.data.model.PayBill;
import cn.hmsoft.pay.data.model.PayMerchantChannel;
import cn.hmsoft.pay.impl.PayWeiXinChannelImpl;
import cn.hmsoft.pay.impl.weixin.entity.GenerateOrderRequestEntity;
import cn.hmsoft.pay.impl.weixin.entity.GenerateOrderResponseEntity;
import cn.hmsoft.pay.impl.weixin.entity.GenerateOrderResultEntity;
import cn.hmsoft.pay.impl.weixin.entity.GenerateRefundRequestEntity;
import cn.hmsoft.pay.impl.weixin.entity.GenerateRefundResponseEntity;
import cn.hmsoft.pay.impl.weixin.entity.QrCodePushEntity;
import cn.hmsoft.pay.util.PayModuleConfig;
import cn.hmsoft.pay.util.PayParamUtil;

public class WeiXinPayUtil {
	
	public final static String ChannelId = "WeiXin";

	/************************************
	 * 生成支付订单
	 * 
	 * @param merchant_id 商户编号
	 * @param channel_id 渠道编号
	 * @param fee_amount 订单金额
	 * @param customer_id 客户编号
	 * @param business_type 业务类型
	 * @param business_id 业务编号
	 * @param product_name 商品名称
	 * @return
	 */
	public static PayBill makePayBill(String merchant_id, String channel_id,
			int order_amount, String customer_id, String business_type,
			String business_id, String product_name) {
		return makePayBill(merchant_id, channel_id, null, order_amount,
				customer_id, business_type, business_id, product_name);
	}

	/************************************
	 * 生成支付订单
	 * 
	 * @param merchant_id 商户编号
	 * @param channel_id 渠道编号
	 * @param fee_amount 订单金额
	 * @param customer_id 客户编号
	 * @param business_id 业务编号
	 * @return
	 */
	public static PayBill makePayBill(String merchant_id, String channel_id,
			int order_amount, String customer_id, String business_id) {
		return makePayBill(merchant_id, channel_id, null, order_amount,
				customer_id, PayParamUtil.DefaultBusinessType, business_id,
				null);

	}

	/************************************
	 * 生成支付订单
	 * 
	 * @param merchant_id 商户编号
	 * @param channel_id 渠道编号
	 * @param open_id 公众号
	 * @param fee_amount 订单金额
	 * @param customer_id 客户编号
	 * @param business_id 业务编号
	 * @return
	 */
	public static PayBill makePayBill(String merchant_id, String channel_id,
			String open_id, int order_amount, String customer_id,
			String business_id) {
		return makePayBill(merchant_id, channel_id, open_id, order_amount,
				customer_id, PayParamUtil.DefaultBusinessType, business_id,
				null);

	}

	/************************************
	 * 生成支付订单
	 * 
	 * @param merchant_id 商户编号
	 * @param channel_id 渠道编号
	 * @param fee_amount 订单金额
	 * @param customer_id 客户编号
	 * @param business_type 业务类型
	 * @param business_id 业务编号
	 * @return
	 */
	public static PayBill makePayBill(String merchant_id, String channel_id,
			int order_amount, String customer_id, String business_type,
			String business_id) {
		return makePayBill(merchant_id, channel_id, null, order_amount,
				customer_id, business_type, business_id, null);

	}

	/************************************
	 * 生成支付订单
	 * 
	 * @param merchant_id 商户编号
	 * @param channel_id 渠道编号
	 * @param open_id 公众号
	 * @param fee_amount 订单金额
	 * @param customer_id 客户编号
	 * @param business_type 业务类型
	 * @param business_id 业务编号
	 * @return
	 */
	public static PayBill makePayBill(String merchant_id, String channel_id,
			String open_id, int order_amount, String customer_id,
			String business_type, String business_id) {
		return makePayBill(merchant_id, channel_id, open_id, order_amount,
				customer_id, business_type, business_id, null);

	}

	/************************************
	 * 生成支付订单
	 * 
	 * @param merchant_id 商户编号
	 * @param channel_id 渠道编号
	 * @param open_id 公众号
	 * @param fee_amount 订单金额
	 * @param customer_id 客户编号
	 * @param business_type 业务类型
	 * @param business_id 业务编号
	 * @param product_name 商品名称
	 * @return
	 */
	public static PayBill makePayBill(String merchant_id, String channel_id,
			String open_id, int order_amount, String customer_id,
			String business_type, String business_id, String product_name) {
		if(StringHelper.isEmpty(merchant_id))
			throw new BusinessException("创建支付订单失败：商户编号为空！");
		if(StringHelper.isEmpty(channel_id))
			throw new BusinessException("创建支付订单失败：支付方式为空！");
		PayWeiXinChannelImpl payImpl = (PayWeiXinChannelImpl) PayModuleConfig
				.getPayImpl(merchant_id, channel_id);
		FrameAssertUtil.isNotNull(payImpl, "该商户指定的支付渠道未能成功加载，请联系技术部门");
		return payImpl.makePayBill(open_id, order_amount, customer_id,
				business_type, business_id, product_name);

	}

	/*************************
	 * 签名
	 */
	public static String sign(Object entity, PayMerchantChannel mc) {
		Map<String, Object> map = BeanHelper.toMap(entity);
		TreeMap<String, String> signMap = new TreeMap<String, String>();
		for (String key : map.keySet()) {
			if (map.get(key) == null)
				continue;
			String value = map.get(key).toString();
			if (StringHelper.isEmpty(value))
				continue;
			signMap.put(key, value);
		}
		// 拼凑字符串
		StringBuilder sb = new StringBuilder();
		for (String key : signMap.keySet()) {
			if ("sign".equals(key))
				continue;
			sb.append(key + "=" + signMap.get(key) + "&");
		}
		sb.append("key=" + mc.getBank_merchant_pass());
		String value = SecureHelper.md5(sb.toString());
		return value;

	}

	public static String makeXmlFromEntity(Object entity) {
		try {
			JAXBContext jc = JAXBContext.newInstance(entity.getClass());
			Marshaller m = jc.createMarshaller();
			// 是否格式化生成的xml串
			// m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
			// 是否省略xml头信息
			m.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
			// 编码格式
			m.setProperty(Marshaller.JAXB_ENCODING, "GBK");
			// 理论上应该试用sun的类库处理特殊字符，但目前版本存在bug，临时办法处理，先放到字符串中，然后替换掉特殊字符
			StringWriter swriter = new StringWriter();
			m.marshal(entity, swriter);
			return swriter.getBuffer().toString().replaceAll("&lt;", "<")
					.replaceAll("&gt;", ">").replaceAll("&amp;", "&");
		} catch (Exception e) {
			e.printStackTrace();
			throw new BusinessException("无法将对象转换为xml字符串");
		}
	}

	public static void writeScanError(PayMerchantChannel mc, String msg,
			HttpServletResponse response) {
		GenerateOrderResultEntity entity = new GenerateOrderResultEntity(mc);
		entity.setResult_code("FAIL");
		entity.setErr_code_des(msg);
		String xmlString = makeXmlFromEntity(entity);
		try {
			response.setHeader("Content-type", "text/html;charset=utf-8");
			response.getOutputStream().write(xmlString.getBytes("utf-8"));
			response.getOutputStream().flush();
			response.getOutputStream().close();
		} catch (Exception e) {
		}
	}

	public static void writeScanResult(PayMerchantChannel mc, String prepay_id,
			HttpServletResponse response) {
		GenerateOrderResultEntity entity = new GenerateOrderResultEntity(mc);
		entity.setPrepay_id(prepay_id);
		entity.setSign(sign(entity, mc));
		String xmlString = makeXmlFromEntity(entity);

		try {
			response.setHeader("Content-type", "text/html;charset=utf-8");
			response.getOutputStream().write(xmlString.getBytes("utf-8"));
			response.getOutputStream().flush();
			response.getOutputStream().close();
		} catch (Exception e) {
		}
	}

	public static QrCodePushEntity checkQrCodeRequest(PayMerchantChannel mc,
			QrCodePushEntity entity, HttpServletResponse response) {
		if (entity == null) {
			writeScanError(mc, "无法正确解析提交的参数信息", response);
			return null;
		}

		String sign = sign(entity, mc);
		if (!sign.equals(entity.getSign())) {
			writeScanError(mc, "签名错误", response);
			return null;
		}
		return entity;
	}

	@SuppressWarnings("unchecked")
	public static <T> T sendXmlRequest(String url, Object entity, Class<T> clazz) {
		try {
			URL rUrl = new URL(url);
			String returnXmlString = WeiXinPayUtil.makeXmlFromEntity(entity);
			HttpURLConnection http = (HttpURLConnection) rUrl.openConnection();
			// get还是post方式
			http.setRequestMethod("POST");
			// 允许输出
			http.setDoOutput(true);
			// 允许传入
			http.setDoInput(true);
			http.setRequestProperty("Connection", "Keep-Alive");
			http.setRequestProperty("Charset", "UTF-8");
			try {
				http.connect();
				PrintWriter out = new PrintWriter(new OutputStreamWriter(
						http.getOutputStream(), "UTF-8"));
				out.print(returnXmlString);
				out.flush();
				out.close();
			} catch (Exception e) {
				// e.printStackTrace();
			}
			// 连接
			String message = null;
			try {
				InputStream is = http.getInputStream();
				int size = is.available();
				byte[] jsonBytes = new byte[size];
				is.read(jsonBytes);
				message = new String(jsonBytes, "UTF-8");
				is.close();
			} catch (Exception e) {
				e.printStackTrace();

			} finally {
				try {
					http.disconnect();
					http = null;
				} catch (Exception e) {

				}
			}
			//System.out.println(message);
			JAXBContext jc = JAXBContext
					.newInstance(GenerateOrderResponseEntity.class);
			Unmarshaller u = jc.createUnmarshaller();
			T msg = (T) u.unmarshal(new StringReader(message));
			return msg;
		} catch (Exception e) {
			e.printStackTrace();
			throw new BusinessException("业务处理错误，请稍后重试");
		}
	}

	// 验证签名
	public static void checkSign(PayMerchantChannel mc, Object entity,
			String sign) {
		String v = sign(entity, mc);
		FrameAssertUtil.isEqual(sign, v, "验证签名失败");
	}

	/************************************
	 * 创建支付订单
	 * 
	 * @param entity
	 * @param mc
	 * @return
	 */
	public static GenerateOrderResponseEntity requestMakeOrder(
			GenerateOrderRequestEntity entity, PayMerchantChannel mc) {
		entity.setSign(WeiXinPayUtil.sign(entity, mc));
		GenerateOrderResponseEntity result = WeiXinPayUtil.sendXmlRequest(
				"https://api.mch.weixin.qq.com/pay/unifiedorder", entity,
				GenerateOrderResponseEntity.class);
		return result;
	}

	/************************************
	 * 创建退款订单
	 */
	public static GenerateRefundResponseEntity requestMakeOrder(
			GenerateRefundRequestEntity entity, PayMerchantChannel mc) {
		entity.setSign(WeiXinPayUtil.sign(entity, mc));
		GenerateRefundResponseEntity result = WeiXinPayUtil.sendXmlRequest(
				"https://api.mch.weixin.qq.com/secapi/pay/refund", entity,
				GenerateRefundResponseEntity.class);
		return result;
	}

	/***********************************
	 * 生成支付二维码
	 */
	public static void makeQrCode(PayWeiXinChannelImpl impl,String code, OutputStream stream) {
		// String url = "weixin://wxpay/bizpayurl?sr=" + code;
		QrCodeEntity entity = new QrCodeEntity();
		entity.setQrContent(code);
		entity.setScaleImage(impl.getWeiXinImage());
		QrCodeHelper.encode(entity, stream);
	}
}
