package order

import (
	"context"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/beego/beego/v2/client/orm"
	"github.com/beego/beego/v2/core/logs"
)

type OrderInfo struct {
	Id                 int
	IsIpRestricted     int
	IsReplace          int
	ShopName           string  // 商品名称
	OrderPeriod        string  // 订单有效时间
	MerchantOrderId    string  // 商户订单id
	BankOrderId        string  // 本系统订单id
	BankTransId        string  // 上游流水id
	OrderAmount        float64 // 订单提交的金额
	ShowAmount         float64 // 待支付的金额
	FactAmount         float64 // 用户实际支付金额
	RollPoolCode       string  // 轮询池编码
	RollPoolName       string  // 轮询池名称
	RoadUid            string  // 通道标识
	RoadName           string  // 通道名称
	PayProductName     string  // 上游支付公司的名称
	PayProductCode     string  // 上游支付公司的编码代号
	PayTypeCode        string  // 支付产品编码
	PayTypeName        string  // 支付产品名称
	OsType             string  // 操作系统类型
	Status             string  // 订单支付状态
	Refund             string  // 退款状态
	RefundTime         string  // 退款操作时间
	Freeze             string  // 冻结状态
	FreezeTime         string  // 冻结时间
	Unfreeze           string  // 是否已经解冻
	UnfreezeTime       string  // 解冻时间
	NotifyUrl          string  // 下游回调地址
	MerchantUid        string  // 商户id
	MerchantName       string  // 商户名称
	AgentUid           string  // 该商户所属代理
	AgentName          string  // 该商户所属代理名称
	ExValue            string  // 扩展属性
	CardData           string
	Operator           string // 操作人
	CardReturnData     string // 卡片返回数据
	Ip                 string
	TransactionType    string
	PayUrl             string
	UpdateTime         time.Time
	CreateTime         time.Time
	PayTime            time.Time // 用户支付时间
	CardReturnTime     time.Time
	IsSucceedByReplace int
	SendCount          int
	IsAmountDifferent  int
	SendRecord         string
	PoolOrderId        string
}

type OrderIdInfo struct {
	Id          int
	BankOrderId string //本系统订单id
}

const ORDER_INFO = "order_info"

func BankOrderIdIsExist(ctx context.Context, bankOrderId string) bool {
	o := orm.NewOrm()
	exists := o.QueryTable(ORDER_INFO).Filter("bank_order_id", bankOrderId).ExistWithCtx(ctx)
	return exists
}

// UpdateBlockStatus 更新订单拉黑状态
func UpdateBlockStatus(ctx context.Context, bankOrderId string, blockStatus bool) (err error) {
	o := orm.NewOrm()
	_, err = o.QueryTable(ORDER_INFO).Filter("bank_order_id", bankOrderId).UpdateWithCtx(ctx, orm.Params{
		"is_block": blockStatus,
	})
	return
}

func GetOrderLenByMap(ctx context.Context, params map[string]string) int {
	o := orm.NewOrm()
	qs := o.QueryTable(ORDER_INFO)
	for k, v := range params {
		if len(v) > 0 {
			qs = qs.Filter(k, v)
		}
	}
	cnt, _ := qs.OrderBy("-create_time").Limit(-1).CountWithCtx(ctx)
	return int(cnt)
}

func GetOrderByMap(ctx context.Context, params map[string]string, display, offset int64) []OrderInfo {
	o := orm.NewOrm()
	var orderInfoList []OrderInfo
	qs := o.QueryTable(ORDER_INFO)
	for k, v := range params {
		if k == "card_data" {
			continue
		}
		if len(v) > 0 {
			qs = qs.Filter(k, v)
		}
	}
	_, err := qs.Limit(display, offset).OrderBy("-create_time").AllWithCtx(ctx, &orderInfoList)
	if err != nil {
		logs.Error("get order by map fail: ", err)
	}
	return orderInfoList
}

func GetOrderCountByMap(ctx context.Context, params map[string]string) (count int64) {
	o := orm.NewOrm()
	qs := o.QueryTable(ORDER_INFO)
	for k, v := range params {
		if k == "card_data" {
			continue
		}
		if len(v) > 0 {
			qs = qs.Filter(k, v)
		}
	}
	count, err := qs.OrderBy("-create_time").CountWithCtx(ctx)
	if err != nil {
		logs.Error("get order by map fail: ", err)
	}
	return
}

func GetOrderRawSummary(ctx context.Context, params map[string]string) (data []OrderSummaryOutput, err error) {
	o := orm.NewOrm()
	sql := `SELECT
	  road_uid,
	  SUM(order_amount) AS total_order_amount,
	  COUNT(id) AS total_order_count,
	  SUM(CASE WHEN STATUS = 'success' THEN order_amount ELSE 0 END) AS success_fact_amount,
	  SUM(CASE WHEN STATUS = 'success' THEN 1 ELSE 0 END) AS success_fact_count,
	  SUM(CASE WHEN STATUS = 'success' THEN 1 ELSE 0 END) / COUNT(id) AS success_fact_avg
	FROM
	  order_info
	`
	var sqlArgs []interface{}
	if len(params) > 0 {
		subSQL := ""
		for k, v := range params {
			if v == "" {
				continue
			}
			switch k {
			case "create_time__gte":
				subSQL += "create_time >= ? AND "
				sqlArgs = append(sqlArgs, v)
			case "create_time__lte":
				subSQL += "create_time <= ? AND "
				sqlArgs = append(sqlArgs, v)
			case "merchant_name__icontains":
				subSQL += "merchant_name LIKE ? AND "
				sqlArgs = append(sqlArgs, "%"+v+"%")
			case "merchant_order_id__icontains":
				subSQL += "merchant_order_id LIKE ? AND "
				sqlArgs = append(sqlArgs, "%"+v+"%")
			case "bank_order_id__icontains":
				subSQL += "bank_order_id LIKE ? AND "
				sqlArgs = append(sqlArgs, "%"+v+"%")
			case "card_return_data__icontains":
				subSQL += "card_return_data LIKE ? AND "
				sqlArgs = append(sqlArgs, "%"+v+"%")
			case "ex_value__icontains":
				subSQL += "ex_value LIKE ? AND "
				sqlArgs = append(sqlArgs, "%"+v+"%")
			default:
				if !strings.Contains(k, "__") {
					subSQL += k + " = ? AND "
					sqlArgs = append(sqlArgs, v)
				}
			}
		}
		if len(subSQL) > 0 {
			sql += "WHERE " + subSQL
		}
	}
	sql = strings.TrimRight(sql, " AND ")
	sql += `
	GROUP BY
	  road_uid`
	_, err = o.RawWithCtx(ctx, sql, sqlArgs...).QueryRows(&data)
	if errors.Is(err, orm.ErrNoRows) {
		err = nil
	}
	return
}

func GetOrderProfitRawSummary(ctx context.Context, params map[string]string) (platformProfit float64, err error) {
	o := orm.NewOrm()

	sql := `
	SELECT
		SUM(platform_profit) AS platform_profit
	FROM
		order_profit_info
	LEFT JOIN order_info ON order_profit_info.bank_order_id = order_info.bank_order_id
  	WHERE
  		order_info.status=? AND `
	var sqlArgs []interface{}
	sqlArgs = append(sqlArgs, "success")
	if len(params) > 0 {
		subSQL := ""
		for k, v := range params {
			if v == "" {
				continue
			}
			switch k {
			case "create_time__gte":
				subSQL += "order_profit_info.create_time >= ? AND "
				sqlArgs = append(sqlArgs, v)
			case "create_time__lte":
				subSQL += "order_profit_info.create_time <= ? AND "
				sqlArgs = append(sqlArgs, v)
			case "merchant_name__icontains":
				subSQL += "order_profit_info.merchant_name LIKE ? AND "
				sqlArgs = append(sqlArgs, "%"+v+"%")
			case "merchant_order_id__icontains":
				subSQL += "order_info.merchant_order_id LIKE ? AND "
				sqlArgs = append(sqlArgs, "%"+v+"%")
			case "bank_order_id__icontains":
				subSQL += "order_info.bank_order_id LIKE ? AND "
				sqlArgs = append(sqlArgs, "%"+v+"%")
			case "ex_value__icontains":
				subSQL += "order_info.ex_value LIKE ? AND "
				sqlArgs = append(sqlArgs, "%"+v+"%")
			default:
				if !strings.Contains(k, "__") {
					subSQL += "order_info.`" + k + "` = ? AND "
					sqlArgs = append(sqlArgs, v)
				}
			}
		}
		if len(subSQL) > 0 {
			sql += subSQL
		}
	}
	sql = strings.TrimRight(sql, " AND ")
	sql += `
	GROUP BY
	  road_uid`
	platformProfit = 0.0
	err = o.RawWithCtx(ctx, sql, sqlArgs...).QueryRow(&platformProfit)
	if errors.Is(err, orm.ErrNoRows) {
		return 0.0, nil
	}
	return
}

func GetOrderAllByMap(ctx context.Context, params map[string]string) []OrderInfo {
	o := orm.NewOrm()
	var orderInfoList []OrderInfo
	qs := o.QueryTable(ORDER_INFO)
	for k, v := range params {
		if k == "card_data" {
			continue
		}
		if len(v) > 0 {
			qs = qs.Filter(k, v)
		}
	}
	_, err := qs.OrderBy("-create_time").AllWithCtx(ctx, &orderInfoList)
	if err != nil {
		logs.Error("get order by map fail: ", err)
	}
	return orderInfoList
}

func GetOrderIdByMap(ctx context.Context, params map[string]string, display, offset int) []OrderIdInfo {
	o := orm.NewOrm()
	var orderInfoList []OrderIdInfo
	qs := o.QueryTable(ORDER_INFO)
	for k, v := range params {
		if k == "card_data" {
			continue
		}
		if len(v) > 0 {
			qs = qs.Filter(k, v)
		}
	}
	_, err := qs.Limit(display, offset).OrderBy("-create_time").AllWithCtx(ctx, &orderInfoList)
	if err != nil {
		logs.Error("get order by map fail: ", err)
	}
	return orderInfoList
}

func GetAllOrderByMap(ctx context.Context, params map[string]string) []OrderInfo {
	o := orm.NewOrm()
	var orderInfoList []OrderInfo
	qs := o.QueryTable(ORDER_INFO)
	for k, v := range params {
		if k == "card_data" {
			continue
		}
		if len(v) > 0 {
			qs = qs.Filter(k, v)
		}
	}
	_, err := qs.OrderBy("-create_time").AllWithCtx(ctx, &orderInfoList)
	if err != nil {
		logs.Error("get order by map fail: ", err)
	}
	return orderInfoList
}

// GetToDayInfo 获得今日数据
func GetToDayInfo(ctx context.Context) string {
	var params = map[string]string{}
	//params["create_time__gte"] = startTime
	//params["create_time__lte"] = endTime
	o := orm.NewOrm()
	qs := o.QueryTable(ORDER_INFO)
	for k, v := range params {
		if len(v) > 0 {
			qs = qs.Filter(k, v)
		}
	}

	successRate := "0%"
	allCount, _ := qs.Limit(-1).CountWithCtx(ctx)
	successCount, _ := qs.Filter("status", "success").Limit(-1).CountWithCtx(ctx)
	if allCount == 0 {
		return successRate
	}
	tmp := float64(successCount) / float64(allCount) * 100
	successRate = fmt.Sprintf("%.1f", tmp)
	return successRate + "%"
}

// GetSuccessRateByMap 获得成功率
func GetSuccessRateByMap(ctx context.Context, params map[string]string) string {
	o := orm.NewOrm()
	qs := o.QueryTable(ORDER_INFO)
	for k, v := range params {
		if len(v) > 0 {
			qs = qs.Filter(k, v)
		}
	}

	successRate := "0%"
	allCount, _ := qs.Limit(-1).CountWithCtx(ctx)
	successCount, _ := qs.Filter("status", "success").Limit(-1).CountWithCtx(ctx)
	if allCount == 0 {
		return successRate
	}
	tmp := float64(successCount) / float64(allCount) * 100
	successRate = fmt.Sprintf("%.1f", tmp)
	return successRate + "%"
}

func GetAllAmountByMap(ctx context.Context, params map[string]string) float64 {
	o := orm.NewOrm()
	condition := "select sum(order_amount) as allAmount from order_info "
	for _, v := range params {
		if len(v) > 0 {
			condition = condition + "where "
			break
		}
	}
	flag := false
	if params["create_time__gte"] != "" {
		flag = true
		condition = condition + " create_time >= '" + params["create_time__gte"] + "'"
	}
	if params["create_time__lte"] != "" {
		if flag {
			condition = condition + " and "
		}
		condition = condition + " create_time <= '" + params["create_time__lte"] + "'"
	}
	if params["merchant_name__icontains"] != "" {
		if flag {
			condition = condition + " and "
		}
		condition = condition + "merchant_name like %'" + params["merchant_name__icontains"] + "'% "
	}
	if params["merchant_order_id"] != "" {
		if flag {
			condition = condition + " and "
		}
		condition = condition + " merchant_order_id = '" + params["merchant_order_id"] + "'"
	}
	if params["bank_order_id"] != "" {
		if flag {
			condition = condition + " and "
		}
		condition = condition + " bank_order_id = '" + params["bank_order_id"] + "'"
	}
	if params["status"] != "" {
		if flag {
			condition = condition + " and "
		}
		condition = condition + "status = '" + params["status"] + "'"
	}
	if params["pay_product_code"] != "" {
		if flag {
			condition = condition + " and "
		}
		condition = condition + "pay_product_code = " + params["pay_product_code"] + "'"
	}
	if params["pay_type_code"] != "" {
		if flag {
			condition = condition + " and "
		}
		condition = condition + "pay_type_code = " + params["pay_type_code"]
	}
	logs.Info("get order amount str = ", condition)
	var maps []orm.Params
	allAmount := 0.00
	num, err := o.Raw(condition).Values(&maps)
	if err == nil && num > 0 {
		if maps[0]["allAmount"] != nil {
			allAmount, _ = strconv.ParseFloat(maps[0]["allAmount"].(string), 64)
		}
	}
	return allAmount
}

func GetOrderByBankOrderId(ctx context.Context, bankOrderId string) OrderInfo {
	o := orm.NewOrm()
	var orderInfo OrderInfo
	_, err := o.QueryTable(ORDER_INFO).Filter("bank_order_id", bankOrderId).Limit(1).AllWithCtx(ctx, &orderInfo)
	if err != nil {
		logs.Error("get order info by bankOrderId fail: ", err)
	}
	return orderInfo
}

func GetOneOrder(ctx context.Context, bankOrderId string) OrderInfo {
	o := orm.NewOrm()
	var orderInfo OrderInfo
	_, err := o.QueryTable(ORDER_INFO).Filter("bank_order_id", bankOrderId).Limit(1).AllWithCtx(ctx, &orderInfo)
	if err != nil {
		logs.Error("get one order fail: ", err)
	}

	return orderInfo
}

func GetOrderByTime(ctx context.Context, startTime, endTime string) ([]OrderInfo, error) {
	var orderInfo []OrderInfo
	params := make(map[string]string)

	params["create_time__gte"] = startTime
	params["create_time__lte"] = endTime

	o := orm.NewOrm()
	qs := o.QueryTable(ORDER_INFO)

	for k, v := range params {
		if len(v) > 0 {
			qs = qs.Filter(k, v)
		}
	}
	_, err := qs.AllWithCtx(ctx, &orderInfo)

	return orderInfo, err
}
