package controllers

import (
	"XT_New/enums"
	"XT_New/models"
	"XT_New/service"
	"bytes"
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"github.com/astaxie/beego"
	"io/ioutil"
	"math/rand"
	"net/http"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"
)

const (
	pay_url = "https://api.mch.weixin.qq.com/pay/unifiedorder"
)

type PayApiController struct {
	BaseServeAPIController
	parameters       map[string]string
	resultParameters map[string]string
	payUrl           string
}

type PayApiController2 struct {
	beego.Controller
	parameters       map[string]string
	resultParameters map[string]string
	payUrl           string
}

func PayApiRegistRouters() {
	beego.Router("/api/my/service", &PayApiController{}, "Get:GetMyService")
	beego.Router("/api/product", &PayApiController{}, "Get:GetProduct")
	beego.Router("/api/pay", &PayApiController{}, "Get:GetPayUrl")
	beego.Router("/api/pay/transfer", &PayApiController{}, "Post:PostTransferStatus")
	beego.Router("/api/order", &PayApiController{}, "Post:PostOrderInfo")
	beego.Router("/api/order/get", &PayApiController{}, "Get:GetOrderInfo")
	beego.Router("/api/pay/notify", &PayApiController2{}, "Post:WxPaySuccessNotify")
	beego.Router("/api/order/cancel", &PayApiController{}, "Post:CancelOrder")
	beego.Router("/api/order/list", &PayApiController{}, "Get:GetOrderList")
	beego.Router("/api/order/hetong", &PayApiController{}, "Get:GetHeTong")
	beego.Router("/api/order/hetong", &PayApiController{}, "Post:CreateHeTong")

}
func (c *PayApiController) GetOrderList() {
	adminUserInfo := c.GetAdminUserInfo()
	serviceOrderList, err := service.GetOrderList(adminUserInfo.CurrentOrgId)
	if err == nil {
		c.ServeSuccessJSON(map[string]interface{}{
			"list": serviceOrderList,
		})
	} else {
		c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeServeNotExist)
	}

}

func (c *PayApiController) CancelOrder() {
	adminUserInfo := c.GetAdminUserInfo()
	orderId, _ := c.GetInt64("id", 0)
	if orderId == 0 {
		c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeParamWrong)
		return
	}
	order, err := service.FindServeOrderByID(adminUserInfo.CurrentOrgId, orderId)
	if err != nil {
		c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException)
		return
	}
	if order == nil {
		c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeServeNotExist)
		return
	}

	errs := service.UpdateOrderStatus(adminUserInfo.CurrentOrgId, orderId)
	if errs == nil {
		c.ServeSuccessJSON(map[string]interface{}{
			"msg": "取消成功",
		})
	} else {
		c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeServeNotExist)
	}
}

func (c *PayApiController) PostTransferStatus() {
	adminUserInfo := c.GetAdminUserInfo()
	orderId, _ := c.GetInt64("id", 0)
	if orderId == 0 {
		c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeParamWrong)
		return
	}
	order, err := service.FindServeOrderByID(adminUserInfo.CurrentOrgId, orderId)
	if err != nil {
		c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException)
		return
	}
	if order == nil {
		c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeServeNotExist)
		return
	}

	errs := service.UpdateOrderPayType(adminUserInfo.CurrentOrgId, orderId)

	if errs == nil {
		c.ServeSuccessJSON(map[string]interface{}{
			"msg": "确认成功",
		})
	} else {
		c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeServeNotExist)
	}
}

func (c *PayApiController) GetOrderInfo() {
	adminUserInfo := c.GetAdminUserInfo()
	orderId, _ := c.GetInt64("id", 0)
	order, err := service.FindServeOrderByID(adminUserInfo.CurrentOrgId, orderId)
	if err != nil {
		c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException)
		return
	}
	if order == nil {
		c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeServeNotExist)
		return
	}

	orderInfo, err := service.FindOrderInfomationByID(order.OrderNumber, adminUserInfo.CurrentOrgId)
	if err != nil {
		c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException)
		return
	}
	if orderInfo == nil {
		c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeServeNotExist)
		return
	}
	c.ServeSuccessJSON(map[string]interface{}{
		"total":       order.PaymentAmount,
		"orderNumber": order.OrderNumber,
		"OrgName":     adminUserInfo.Orgs[adminUserInfo.CurrentOrgId].OrgName,
		"order":       order,
		"orderInfo":   orderInfo,
	})
}

func (c *PayApiController) PostOrderInfo() {
	adminUserInfo := c.GetAdminUserInfo()
	amount, _ := c.GetInt64("amount", 0)
	productId, _ := c.GetInt64("id", 0)

	product, err := service.FindProductByID(productId)
	if err != nil {
		c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException)
		return
	}
	if product == nil {
		c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeProductError)
		return
	}
	timestamp := c.GetTimestamp()
	//自定义订单号
	bill_no := "S" + strconv.FormatInt(timestamp, 10) + strconv.FormatInt(adminUserInfo.CurrentOrgId, 10)

	order := models.ServeOrder{
		OrgId:           adminUserInfo.CurrentOrgId,
		Period:          amount * 12,
		Status:          1,
		CreatedTime:     time.Now().Unix(),
		UpdatedTime:     time.Now().Unix(),
		OrderNumber:     bill_no,
		OrderStatus:     1,
		PayableAmount:   float64(amount) * product.Price,
		PaymentAmount:   float64(amount) * product.Price,
		ServeName:       product.ServeName,
		ServeDesc:       product.ServeDesc,
		OrderExpireTime: time.Now().Unix() + (7 * 24 * 3600),
		Quantity:        amount,
		Price:           product.Price,
		ServeId:         productId,
	}
	infomation := models.ServeOrderInfomation{
		OrgId:       adminUserInfo.CurrentOrgId,
		OrderNumber: bill_no,
		Status:      1,
		ProductId:   product.ID,
		ProductName: product.ServeName,
		ProductDesc: product.ServeDesc,
		Price:       product.Price,
		Quantity:    amount,
		MarketPrice: product.OriginalPrice,
	}

	service.CreateOrderRecord(&order)
	service.CreateOrderInfomation(&infomation)
	c.ServeSuccessJSON(map[string]interface{}{
		"order": order,
		"msg":   "提交订单成功",
	})
}

func (c *PayApiController) GetProduct() {
	adminUserInfo := c.GetAdminUserInfo()
	products, err := service.FindAllProduct()
	subscibe, _ := service.FindServiceSubscibeByOrgId(adminUserInfo.CurrentOrgId)

	if err == nil {
		c.ServeSuccessJSON(map[string]interface{}{
			"products":    products,
			"subscibe":    subscibe,
			"serviceTime": time.Now().Unix(),
			//"OrgName":  adminUserInfo.Orgs[adminUserInfo.CurrentOrgId].OrgName,
		})
	} else {
		c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeSystemError)
	}
}

func (c *PayApiController) GetMyService() {
	adminUserInfo := c.GetAdminUserInfo()
	subscibe, err := service.FindServiceSubscibeByOrgId(adminUserInfo.CurrentOrgId)
	if err == nil {
		c.ServeSuccessJSON(map[string]interface{}{
			"subscibe":    subscibe,
			"serviceTime": time.Now().Unix(),
		})
	} else {

	}

}

func (c *PayApiController) GetPayUrl() {
	adminUserInfo := c.GetAdminUserInfo()
	orderId, _ := c.GetInt64("id", 0)
	if orderId == 0 {
		c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeParamWrong)
		return
	}
	order, err := service.FindServeOrderByID(adminUserInfo.CurrentOrgId, orderId)
	if err != nil {
		c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException)
		return
	}
	if order == nil {
		c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeServeNotExist)
		return
	}

	orderInfo, err := service.FindOrderInfomationByID(order.OrderNumber, adminUserInfo.CurrentOrgId)
	if err != nil {
		c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException)
		return
	}
	if orderInfo == nil {
		c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeServeNotExist)
		return
	}

	totalPrice := orderInfo.Price * float64(orderInfo.Quantity)
	totalFee := fmt.Sprintf("%.0f", totalPrice*100)
	c.SetParameter("out_trade_no", orderInfo.OrderNumber)
	c.SetParameter("total_fee", totalFee)
	c.SetParameter("trade_type", "NATIVE")
	c.SetParameter("body", order.ServeName)
	notify_url := beego.AppConfig.String("httpdomain") + "/api/pay/notify"
	c.SetParameter("product_id", orderInfo.OrderNumber)
	c.SetParameter("notify_url", notify_url)
	c.SetParameter("spbill_create_ip", c.GetClientIp())
	fmt.Println(c.GetClientIp())
	url, err := c.GetPayCodeUrl()
	fmt.Println(err)
	if err == nil {
		c.SetPayUrl(url)
		c.ServeSuccessJSON(map[string]interface{}{
			"payUrl": url,
			"price":  order.PayableAmount,
			//":  adminUserInfo.Orgs[adminUserInfo.CurrentOrgId].OrgName,
		})
	} else {
		c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeServeNotExist)
		// utils.LogError(err)
	}

}

func (this *PayApiController2) WxPaySuccessNotify() {

	type PaySuccessXmlResp struct {
		ReturnCode    string `xml:"return_code"`
		Appid         string `xml:"appid"`
		BankType      string `xml:"bank_type"`
		CashFee       int64  `xml:"cash_fee"`
		FeeType       string `xml:"fee_type"`
		IsSubscribe   string `xml:"is_subscribe"`
		MchId         string `xml:"mch_id"`
		NonceStr      string `xml:"nonce_str"`
		OpenId        string `xml:"openid"`
		OutTradeNo    string `xml:"out_trade_no"`
		ResultCode    string `xml:"result_code"`
		Sign          string `xml:"sign"`
		TimeEnd       string `xml:"time_end"`
		TotalFee      string `xml:"total_fee"`
		TradeType     string `xml:"trade_type"`
		TransactionId string `xml:"transaction_id"`
	}

	defer this.Ctx.Request.Body.Close()
	result, _ := ioutil.ReadAll(this.Ctx.Request.Body)

	res := new(PaySuccessXmlResp)
	xmlErr := xml.Unmarshal(result, &res)
	if xmlErr != nil {

	}

	order, _ := service.FindServeOrderByOrderNumber(res.OutTradeNo)
	service.UpdateOrder(order.OrderNumber, order.Quantity, order.OrgId, res.TransactionId, order.PeriodEnd, order.PeriodStart)

	this.SetResultParameter("return_code", res.ReturnCode)
	results := this.ParamsToXml2(this.resultParameters)
	this.Ctx.WriteString(results)
	this.ServeXML()

}

func (this *PayApiController) GetTimestamp() int64 {
	return time.Now().UnixNano() / 1000000 //毫秒
}

func (this *PayApiController) GetClientIp() string {
	ip := this.Ctx.Request.Header.Get("Remote_addr")
	if ip == "" {
		ip = this.Ctx.Request.RemoteAddr
	}
	if strings.Contains(ip, ":") {
		ip = this.Substr(ip, 0, strings.Index(ip, ":"))
	}
	return ip
}

// 截取字符串 start 起点下标 end 终点下标(不包括)
func (this *PayApiController) Substr(str string, start int, end int) string {
	rs := []rune(str)
	length := len(rs)

	if start < 0 || start > length {
		return ""
	}
	if end < 0 || end > length {
		return ""
	}
	return string(rs[start:end])
}

// 设置请求参数
func (this *PayApiController) SetParameter(key string, value string) {
	if this.parameters == nil {
		this.parameters = make(map[string]string)
	}
	this.parameters[key] = value
}

// 设置请求参数
func (this *PayApiController2) SetResultParameter(key string, value string) {
	if this.resultParameters == nil {
		this.resultParameters = make(map[string]string)
	}
	this.resultParameters[key] = value
}

// 设置prepay_id
func (this *PayApiController) SetPayUrl(payUrl string) {
	this.payUrl = payUrl
}

type XmlResp struct {
	Return_code  string `xml:"return_code"`
	Return_msg   string `xml:"return_msg"`
	Result_code  string `xml:"result_code"`
	Err_code     string `xml:"err_code"`
	Err_code_des string `xml:"err_code_des"`
	Prepay_id    string `xml:"prepay_id"`
	Code_url     string `xml:"code_url"`
}

func (this *PayApiController) GetPayCodeUrl() (string, error) {
	strXml, err := this.CreateXml()
	if err != nil {
		return "", this.Error("get pay_code_url error", err)
	}
	result, err := this.http_post(pay_url, strXml)
	if err != nil {
		return "", this.Error("get pay_code_url error", err)
	}

	res := new(XmlResp)
	xmlErr := xml.Unmarshal(result, &res)
	if xmlErr != nil {
		return "", this.Error("get pay_code_url xml error", xmlErr)
	}
	if res.Return_code != "SUCCESS" {
		return "", this.Error("get pay_code_url result error: "+res.Return_msg, nil)
	}

	if res.Result_code != "SUCCESS" {
		return "", this.Error("get pay_code_url result error: "+res.Err_code+"-"+res.Err_code_des, nil)
	}
	if res.Code_url == "" {
		return "", this.Error("get pay_code_url result error: not get pay_code_url", nil)
	}
	return res.Code_url, nil
}

func (this *PayApiController) http_post(url string, xml string) ([]byte, error) {
	bc := &http.Client{
		Timeout: 30 * time.Second, //设置超时时间30s
	}
	res, err := bc.Post(url, "text/xml:charset=UTF-8", strings.NewReader(xml))
	if err != nil {
		return nil, this.Error("post", err)
	}
	result, err := ioutil.ReadAll(res.Body)
	res.Body.Close()
	if err != nil {
		return nil, this.Error("post result err", err)
	}
	return result, nil
}

func (this *PayApiController) CreateXml() (string, error) {
	//检测必填参数
	if this.parameters["out_trade_no"] == "" {
		return "", this.Error("缺少统一支付接口必填参数out_trade_no(商户订单号)!", nil)
	} else if this.parameters["body"] == "" {
		return "", this.Error("缺少统一支付接口必填参数body(商品描述)!", nil)
	} else if this.parameters["total_fee"] == "" {
		return "", this.Error("缺少统一支付接口必填参数total_fee(交易金额)!", nil)
	} else if this.parameters["notify_url"] == "" {
		return "", this.Error("缺少统一支付接口必填参数notify_url(异步接收微信支付结果通知的回调地址)!", nil)
	} else if this.parameters["trade_type"] == "" {
		return "", this.Error("缺少统一支付接口必填参数trade_type(交易类型)!", nil)
	} else if this.parameters["spbill_create_ip"] == "" {
		return "", this.Error("缺少统一支付接口必填参数spbill_create_ip(终端ip)", nil)
	} else if this.parameters["trade_type"] == "NATIVE" && this.parameters["product_id"] == "" {
		return "", this.Error("统一支付接口中,缺少必填参数product_id!trade_type为NATIVE时,product_id为必填参数!", nil)
	}
	this.parameters["appid"] = beego.AppConfig.String("appid")                             //公众账号ID
	this.parameters["mch_id"] = beego.AppConfig.String("mchid")                            //商户号
	this.parameters["nonce_str"] = this.CreateNoncestr(32)                                 //随机字符串
	this.parameters["sign"] = this.GetSign(this.parameters, beego.AppConfig.String("key")) //签名
	return this.ParamsToXml(this.parameters), nil
}

// 产生随机字符串,不长于32位
func (this *PayApiController) CreateNoncestr(length int) (nonceStr string) {
	chars := "abcdefghijklmnopqrstuvwxyz0123456789"
	for i := 0; i < length; i++ {
		idx := rand.Intn(len(chars) - 1)
		nonceStr += chars[idx : idx+1]
	}
	return
}

func (this *PayApiController) Error(strMsg string, err error) error {
	if err == nil {
		return errors.New(strMsg)
	} else {
		return errors.New(strMsg + ": " + err.Error())
	}
}

// 格式化参数,签名过程需要使用
func (this *PayApiController) FormatParams(paramsMap map[string]string) string {
	//STEP 1, 对key进行升序排序.
	var sorted_keys []string
	for k, _ := range paramsMap {
		sorted_keys = append(sorted_keys, k)
	}
	sort.Strings(sorted_keys)

	//STEP2, 对key=value的键值对用&连接起来,略过空值
	var paramsStr []string
	for _, k := range sorted_keys {
		v := fmt.Sprintf("%v", strings.TrimSpace(paramsMap[k]))
		if v != "" {
			paramsStr = append(paramsStr, fmt.Sprintf("%s=%s", k, v))
		}
	}
	return strings.Join(paramsStr, "&")
}

// 生成签名
func (this *PayApiController) GetSign(paramsMap map[string]string, wxKey string) string {
	//STEP 1:按字典序排序参数
	paramsStr := this.FormatParams(paramsMap)
	//STEP 2:在string后加入KEY
	signStr := paramsStr + "&key=" + wxKey
	//STEP 3:MD5加密
	sign := md5.New()
	sign.Write([]byte(signStr))
	//STEP 3:所有字符转为大写
	return strings.ToUpper(hex.EncodeToString(sign.Sum(nil)))
}

// xml结构
func (this *PayApiController) ParamsToXml(data map[string]string) string {
	fmt.Println(data)
	buf := bytes.NewBufferString("<xml>")
	for k, v := range data {
		str := "<![CDATA[%s]]>"
		flag, _ := regexp.MatchString("^\\d+\\.?\\d*$", v)
		if flag {
			str = "%s"
		}
		buf.WriteString(fmt.Sprintf("<%s>"+str+"</%s>", k, v, k))
	}
	buf.WriteString("</xml>")
	return buf.String()
}

// xml结构
func (this *PayApiController2) ParamsToXml2(data map[string]string) string {
	fmt.Println(data)
	buf := bytes.NewBufferString("<xml>")
	for k, v := range data {
		str := "<![CDATA[%s]]>"
		flag, _ := regexp.MatchString("^\\d+\\.?\\d*$", v)
		if flag {
			str = "%s"
		}
		buf.WriteString(fmt.Sprintf("<%s>"+str+"</%s>", k, v, k))
	}
	buf.WriteString("</xml>")
	return buf.String()
}

func (this *PayApiController) SetUrl(payUrl string) {
	this.payUrl = payUrl
}

func (c *PayApiController) GetHeTong() {
	order_id, _ := c.GetInt64("order_id", 0)
	if order_id <= 0 {
		c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeParamWrong)
		return
	}

	admin := c.GetAdminUserInfo()
	ht, err := service.GetHetong(admin.CurrentOrgId, order_id)
	if err != nil {
		c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException)
		return
	}

	c.ServeSuccessJSON(map[string]interface{}{
		"ht": ht,
	})
}

func (c *PayApiController) CreateHeTong() {
	orderId, _ := c.GetInt64("order_id", 0)
	if orderId <= 0 {
		c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeParamWrong)
		return
	}
	admin := c.GetAdminUserInfo()
	order, err := service.FindServeOrderByID(admin.CurrentOrgId, orderId)
	if err != nil {
		c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException)
		return
	}
	if order == nil {
		c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeServeNotExist)
		return
	}
	ht, err := service.GetHetong(admin.CurrentOrgId, orderId)
	if err != nil {
		c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException)
		return
	}
	if ht != nil {
		c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeHetongHad)
		return
	}

	var hetong models.ServeOrderContract
	err = json.Unmarshal(c.Ctx.Input.RequestBody, &hetong)
	if err != nil {
		fmt.Println(err)
		c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeParamWrong)
		return
	}

	hetong.OrderId = orderId
	hetong.OrderNumber = order.OrderNumber
	hetong.CreatedTime = time.Now().Unix()
	hetong.UpdatedTime = time.Now().Unix()
	hetong.Status = 1
	hetong.OrgId = admin.CurrentOrgId

	err = service.CreateHetong(&hetong)
	if err != nil {
		c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeCreateHetongFail)
		return
	}

	c.ServeSuccessJSON(map[string]interface{}{
		"ht": hetong,
	})
}