package controllers

import (
	"ai_developer_admin/libs/kong"
	"ai_developer_admin/libs/mysql"
	"ai_developer_admin/libs/odysseus"
	"ai_developer_admin/libs/postgres"
	"ai_developer_admin/libs/snowflake"
	"ai_developer_admin/models"
	"encoding/json"
	"fmt"
	"github.com/beego/beego/orm"
	"github.com/beego/beego/v2/core/logs"
	"net/http"
	"strconv"
	"time"
)

type FundsController struct {
	MainController
}

func (server *FundsController) Recharge() {
	info, err := server.Check()
	if err != nil {
		server.respond(http.StatusUnauthorized, err.Error())
		return
	}

	var chargeRequest models.ChargeRequest
	body := server.Ctx.Input.RequestBody
	err = json.Unmarshal(body, &chargeRequest) //解析body中数据
	logs.Debug("charge", chargeRequest)
	if err != nil {
		server.respond(models.BusinessFailed, err.Error())
		return
	}

	if info.Role != 1 && info.Role != 2 && chargeRequest.PaymentMethod == models.ManualPay {
		server.respond(models.BusinessFailed, "Please contact the business development")
		return
	}

	checkUser := &models.User{Id: info.UserID}
	cond := "id"
	if chargeRequest.Mail != "" {
		checkUser = &models.User{Mail: chargeRequest.Mail}
		cond = "mail"
	}
	err = mysql.GetMysqlInstace().Ormer.Read(checkUser, cond)
	if err != nil {
		server.respond(models.BusinessFailed, "user not exist")
		return
	}

	timestamp := time.Now()
	amount := int64(chargeRequest.Amount * 1000000)
	//charge := models.ChargeRecord{
	//	UserId:        checkUser.Id,
	//	Status:        1,
	//	PayMethodDesc: chargeRequest.PaymentMethod.String(),
	//	PayMethod:     int(chargeRequest.PaymentMethod),
	//	ChargeTime:    timestamp,
	//	CreatedTime:   timestamp,
	//	UpdatedTime:   timestamp,
	//	Amount:        amount,
	//}

	//mysql.GetMysqlInstace().Ormer.Insert(&charge)

	balance, err := odysseus.GetUserBalance(int64(checkUser.Id))
	if err != nil {
		server.respond(models.BusinessFailed, "failed")
		return
	}

	balance = int64((float64(balance/1000000) + chargeRequest.Amount) * 1000000)

	//sql := "SELECT Max(id) AS count FROM funds;"
	max, _ := snowflake.NextId()
	order, _ := snowflake.NextId()
	orderId := strconv.FormatInt(int64(order), 10)

	//if err != nil {
	//	server.respond(models.BusinessFailed, err.Error())
	//	return
	//}

	tradeTime := fmt.Sprintf(time.Now().UTC().Format(format))
	//fundsData := models.Funds{
	//	Id:            max + 1,
	//	Uid:           info.UserID,
	//	Amount:        amount,
	//	TradeChannel:  int(chargeRequest.PaymentMethod),
	//	ChannelSerial: "",
	//	Status:        4,
	//	TradeTime:     tradeTime,
	//	TradeFlow:     int(models.Income),
	//	TradeType:     int(models.Charge),
	//	Balance:       balance,
	//	Remark:        "",
	//	OrderId:       "",
	//}
	//flag, err := postgres.InsertFunds(&fundsData)

	//todo 如果是三方支付，待支付成功，再更新余额
	sql := fmt.Sprintf("INSERT INTO funds ("+
		"channel_serial,"+
		"order_id,"+
		"remark,"+
		"id, "+
		"status,"+
		"uid,"+
		"trade_flow,"+
		"trade_type,"+
		"trade_channel,"+
		"amount,"+
		"balance,"+
		"trade_time) "+
		"VALUES ("+
		"'%s','%s','%s',"+
		"%d,"+
		"%d,"+
		"%d,"+
		"%d,"+
		"%d,"+
		"%d,"+
		"%d,"+
		"%d,"+
		"'%s');",
		"",
		orderId,
		"",
		max,
		4,
		info.UserID,
		int(models.Income),
		int(models.Charge),
		int(chargeRequest.PaymentMethod),

		amount,
		balance,
		tradeTime)
	flag, err := postgres.InsertWithSql(sql)
	if err != nil {
		server.respond(models.BusinessFailed, err.Error())
		return
	}

	//tempUser := models.User{
	//	Id:      checkUser.Id,
	//	Balance: balance,
	//}
	checkUser.Balance = balance
	checkUser.UpdatedTime = timestamp
	flag, err = mysql.GetMysqlInstace().Ormer.Update(checkUser)
	if err != nil {
		server.respond(http.StatusOK, "failed")
	}
	logs.Debug("更新用户", flag)
	//balanceKey := "balance:" + checkUser.CustomId
	//chargeKey := "charge:user-" + checkUser.CustomId
	//redis.SetKeyAndData(balanceKey, balance, 0)
	//redis.SetKeyAndData(chargeKey, chargeRequest.Amount*1000000, 0)

	userId := checkUser.CustomId
	if userId == "" {
		userId = strconv.Itoa(checkUser.Id)
	}

	//odysseus.SyncBanlace(userId, balance)

	checkUserLevel := &models.UserLevel{Level: checkUser.Level}
	err = mysql.GetMysqlInstace().Ormer.Read(checkUserLevel, "level")
	if err != nil {
		logs.Debug("Recharge 用户等级查找失败")
	} else {
		odysseus.SetUserInfo(checkUser)
		//odysseus.GetUserInfo(int64(checkUser.Id))
		//odysseus.GetUserInfo(int64(checkUser.Id + 1))
		pluginId := checkUser.RateLimitPluginId
		plugin, err := kong.SetRateLimit(checkUser, checkUserLevel, pluginId)
		if err == nil && pluginId == "" {
			checkUser.RateLimitPluginId = plugin.Id
			mysql.GetMysqlInstace().Ormer.Update(checkUser)
		}
	}

	server.respond(http.StatusOK, "success")
}

func (server *FundsController) RechargeRecords() {
	info, err := server.Check()
	if err != nil {
		server.respond(http.StatusUnauthorized, err.Error())
		return
	}

	body := server.Ctx.Input.RequestBody
	appRequest := models.AppRequest{}
	err = json.Unmarshal(body, &appRequest) //解析body中数据
	logs.Debug("appRequest", appRequest, string(body))
	//if err != nil {
	//	server.respond(models.NoRequestBody, err.Error())
	//	return
	//}

	if appRequest.Page == 0 {
		appRequest.Page = 1
	}

	if appRequest.Size == 0 {
		appRequest.Size = 10
	}
	offset := (appRequest.Page - 1) * appRequest.Size

	qs := mysql.GetMysqlInstace().Ormer.QueryTable("charge_record")
	infoQs := qs.Filter("user_id", info.UserID).Offset(offset).Limit(appRequest.Size)
	count, err := infoQs.Count()
	logs.Debug("Count = ", count)
	var tokens []*models.ChargeRecord
	if count > 0 {
		infoQs.All(&tokens)
	}
	total, err := qs.Count()
	responseData := struct {
		Total int64       `json:"total"`
		Data  interface{} `json:"data,omitempty"`
	}{
		Total: total,
		Data:  tokens,
	}
	server.respond(http.StatusOK, "", responseData)

}

func (server *FundsController) IncomeAndExpense() {
	info, err := server.Check()
	if err != nil {
		server.respond(http.StatusUnauthorized, err.Error())
		return
	}

	body := server.Ctx.Input.RequestBody
	appRequest := models.AppRequest{}
	err = json.Unmarshal(body, &appRequest) //解析body中数据
	logs.Debug("appRequest", appRequest, string(body))
	//if err != nil {
	//	server.respond(models.NoRequestBody, err.Error())
	//	return
	//}

	if appRequest.Page == 0 {
		appRequest.Page = 1
	}

	if appRequest.Size == 0 {
		appRequest.Size = 10
	}
	offset := (appRequest.Page - 1) * appRequest.Size
	size := appRequest.Page * appRequest.Size

	if appRequest.StartTime == "" && appRequest.EndTime != "" {
		server.respond(models.MissingParameter, "Missing start time")
		return
	}
	if appRequest.StartTime != "" && appRequest.EndTime == "" {
		server.respond(models.MissingParameter, "Missing end time")
		return
	}

	countQB, _ := orm.NewQueryBuilder("mysql")
	countQB.Select("count(*)").
		From("funds").Where("uid != '0'")

	queryQB, _ := orm.NewQueryBuilder("mysql")
	queryQB.Select("*").
		From("funds").Where("uid != '0'")

	if info.Role != 1 && info.Role != 2 {
		countQB.And(fmt.Sprintf("uid = '%d'", info.UserID))
		queryQB.And(fmt.Sprintf("uid = '%d'", info.UserID))
	}

	if appRequest.StartTime != "" && appRequest.EndTime != "" {
		start, _ := time.Parse(layout, appRequest.StartTime)
		startTime := fmt.Sprintf(start.Format(format))
		end, _ := time.Parse(layout, appRequest.EndTime)
		endTime := fmt.Sprintf(end.Format(format))
		if end.Before(start) {
			server.respond(models.BusinessFailed, "Start time cannot be greater than end time")
			return
		}
		countQB.And(fmt.Sprintf("trade_time >= '%s'", startTime)).And(fmt.Sprintf("trade_time <= '%s'", endTime))
		queryQB.And(fmt.Sprintf("trade_time >= '%s'", startTime)).And(fmt.Sprintf("trade_time <= '%s'", endTime))
	}
	if appRequest.TradeType != 0 {
		countQB.And(fmt.Sprintf("trade_type = %d", appRequest.TradeType))
		queryQB.And(fmt.Sprintf("trade_type = %d", appRequest.TradeType))
	}
	if appRequest.TradeFlow != 0 {
		countQB.And(fmt.Sprintf("trade_flow = %d", appRequest.TradeFlow))
		queryQB.And(fmt.Sprintf("trade_flow = %d", appRequest.TradeFlow))
	}
	if appRequest.TradeChannel != 0 {
		countQB.And(fmt.Sprintf("trade_channel = %d", appRequest.TradeChannel))
		queryQB.And(fmt.Sprintf("trade_channel = %d", appRequest.TradeChannel))
	}

	sql := countQB.String()

	var types []*models.Funds
	//sql := fmt.Sprintf("SELECT count(*) FROM funds WHERE uid = %d %s;", info.UserID, timeCondition)
	//if info.Role == 1 || info.Role == 2 {
	//	if timeCondition != "" {
	//		timeCondition = "WHERE" + timeCondition
	//	}
	//	sql = fmt.Sprintf("SELECT count(*) FROM funds %s;", timeCondition)
	//}
	total, err := postgres.QueryTotal(sql)
	logs.Debug("total = %d", total)
	if total == 0 {
		responseData := struct {
			Total int64       `json:"total"`
			Data  interface{} `json:"data,omitempty"`
		}{
			Total: total,
			Data:  types,
		}
		server.respond(http.StatusOK, "", responseData)
		return
	}

	queryQB.OrderBy("trade_time").Desc()
	sql = fmt.Sprintf("%s LIMIT %d,%d;", queryQB.String(), offset, size)
	//sql = fmt.Sprintf("SELECT * FROM funds WHERE uid = %d %s LIMIT %d,%d;", info.UserID, timeCondition, offset, size)
	//if info.Role == 1 || info.Role == 2 {
	//	sql = fmt.Sprintf("SELECT * FROM funds %s LIMIT %d,%d;", timeCondition, offset, size)
	//}
	data, err := postgres.QueryFunds(sql)
	if err != nil {
		server.respond(models.BusinessFailed, err.Error())
		return
	}
	var ids []int
	for _, fund := range data {
		id, _ := strconv.Atoi(fund.Uid)
		ids = append(ids, id)
	}
	var users []models.User
	if info.Role == 1 || info.Role == 2 {
		_, _ = mysql.GetMysqlInstace().Ormer.QueryTable("user").Filter("id__in", ids).All(&users)
	}

	var responseTypes []models.ResponseFunds
	for _, fund := range data {
		amountInt, _ := strconv.ParseInt(fund.Amount, 10, 64)
		amount := amountInt / 1000000

		balanceInt, _ := strconv.ParseInt(fund.Balance, 10, 64)
		balance := balanceInt / 1000000

		tradeChannel, _ := strconv.Atoi(fund.TradeChannel)
		status, _ := strconv.Atoi(fund.Status)
		tradeFlow, _ := strconv.Atoi(fund.TradeFlow)
		tradeType, _ := strconv.Atoi(fund.TradeType)

		id, _ := strconv.Atoi(fund.Uid)
		username := FindName(users, id)
		responseType := models.ResponseFunds{
			Id:             fund.Id,                                     // int64
			Uid:            fund.Uid,                                    // int
			Amount:         float64(amount),                             // int64
			TradeChannel:   models.PayMethodType(tradeChannel).String(), // int
			TradeChannelEn: models.PayMethodType(tradeChannel).EnString(),
			ChannelSerial:  fund.ChannelSerial, // string
			StatusDesc:     models.PayStatus(status).String(),
			StatusDescEn:   models.PayStatus(status).EnString(), // int
			Status:         status,
			TradeTime:      fund.TradeTime,                           // string
			TradeFlow:      models.TradeFlowType(tradeFlow).String(), // int
			TradeFlowEn:    models.TradeFlowType(tradeFlow).EnString(),
			TradeType:      models.TradeKind(tradeType).String(), // int
			TradeTypeEn:    models.TradeKind(tradeType).EnString(),
			Balance:        float64(balance), // int64
			Remark:         fund.Remark,      // string
			OrderId:        fund.OrderId,
			UserName:       username,
		}
		responseTypes = append(responseTypes, responseType)
	}
	responseData := struct {
		Total int64       `json:"total"`
		Data  interface{} `json:"data,omitempty"`
	}{
		Total: total,
		Data:  responseTypes,
	}
	server.respond(http.StatusOK, "", responseData)
}

func FindName(users []models.User, id int) string {
	if len(users) <= 0 {
		return ""
	}
	for _, value := range users {
		if value.Id == id {
			return value.Name
		}
	}
	return ""
}

func (server *FundsController) Enumeration() {
	_, err := server.Check()
	if err != nil {
		server.respond(http.StatusUnauthorized, err.Error())
		return
	}

	var kinds []*models.EnumType
	for _, value := range [...]models.TradeKind{models.Charge, models.Spending, models.Withdrawal} {
		typeData := models.EnumType{
			Id:     int(value),
			Desc:   value.String(),
			EnDesc: value.EnString(),
		}
		kinds = append(kinds, &typeData)
	}

	var flows []*models.EnumType
	for _, value := range [...]models.TradeFlowType{models.Income, models.Expenditure} {
		typeData := models.EnumType{
			Id:     int(value),
			Desc:   value.String(),
			EnDesc: value.EnString(),
		}
		flows = append(flows, &typeData)
	}

	var statuss []*models.EnumType
	for _, value := range [...]models.PayStatus{models.PendingPay, models.InitiatePay, models.SuccessPay, models.FinishCharge, models.Billed, models.InitiateWithdrawal,
		models.PendingAudit, models.ApprovedAudit, models.RejectedAudit, models.PendingTransfer, models.SuccessTransfer, models.SuccessWithdrawal,
		models.FailurePay, models.FailureTransfer} {
		typeData := models.EnumType{
			Id:     int(value),
			Desc:   value.String(),
			EnDesc: value.EnString(),
		}
		statuss = append(statuss, &typeData)
	}

	var channels []*models.EnumType
	for _, value := range [...]models.PayMethodType{models.WeixinPay, models.AliPay, models.UnionPay,
		models.PayPal, models.ApplePay, models.ManualPay} {
		typeData := models.EnumType{
			Id:     int(value),
			Desc:   value.String(),
			EnDesc: value.EnString(),
		}
		channels = append(channels, &typeData)
	}

	responseData := struct {
		TradeChannels []*models.EnumType `json:"trade_channels,omitempty"`
		TradeTypes    []*models.EnumType `json:"trade_types,omitempty"`
		TradeFlows    []*models.EnumType `json:"trade_flows,omitempty"`
	}{
		TradeChannels: channels,
		TradeTypes:    kinds,
		TradeFlows:    flows,
	}
	server.respond(http.StatusOK, "", responseData)
}
