package controller

import (
	"io"
	"math"
	"net/http"
	"strconv"
	"strings"

	"github.com/QuantumNous/new-api/common"
	"github.com/QuantumNous/new-api/model"
	"github.com/QuantumNous/new-api/service"

	"github.com/gin-gonic/gin"
)

type createDistributorWithdrawalRequest struct {
	AccountType   int    `json:"account_type"`
	RealName      string `json:"real_name"`
	BankName      string `json:"bank_name"`
	BankAccount   string `json:"bank_account"`
	WithdrawMonth string `json:"withdraw_month"`
	// 使用 float64 兼容前端 JSON 中的小数（如 InputNumber），再取整
	QuotaAmount float64 `json:"quota_amount"`
	// 个人扩展
	IdCardNo          string `json:"id_card_no"`
	IdCardExpiry      string `json:"id_card_expiry"`
	Mobile            string `json:"mobile"`
	BankReservedPhone string `json:"bank_reserved_phone"`
	IdCardFrontUrl    string `json:"id_card_front_url"`
	IdCardBackUrl     string `json:"id_card_back_url"`
	BankCardPhotoUrl  string `json:"bank_card_photo_url"`
	// 企业扩展
	CreditCode               string `json:"credit_code"`
	LegalPersonName          string `json:"legal_person_name"`
	LegalPersonPhone         string `json:"legal_person_phone"`
	BankBranchCode           string `json:"bank_branch_code"`
	ContactPerson            string `json:"contact_person"`
	BusinessLicenseUrl       string `json:"business_license_url"`
	CorporateAccountProofUrl string `json:"corporate_account_proof_url"`
	InvoiceUrl               string `json:"invoice_url"`
}

func distributorWithdrawalToJSON(w model.DistributorWithdrawal, username string) gin.H {
	profile := model.ParseDistributorWithdrawalProfile(w.ProfileData)
	return gin.H{
		"id":                         w.Id,
		"user_id":                    w.UserId,
		"username":                   username,
		"account_type":               w.AccountType,
		"real_name":                  w.RealName,
		"bank_name":                  w.BankName,
		"bank_account":               w.BankAccount,
		"profile_data":               profile,
		"voucher_urls":               w.VoucherUrls,
		"withdraw_month":             w.WithdrawMonth,
		"quota_amount":               w.QuotaAmount,
		"status":                     w.Status,
		"reject_reason":              w.RejectReason,
		"reviewer_id":                w.ReviewerId,
		"reviewed_at":                w.ReviewedAt,
		"cancelled_at":               w.CancelledAt,
		"created_at":                 w.CreatedAt,
		"updated_at":                 w.UpdatedAt,
		"id_card_no":                 profile.IdCardNo,
		"id_card_expiry":             profile.IdCardExpiry,
		"mobile":                     profile.Mobile,
		"bank_reserved_phone":        profile.BankReservedPhone,
		"id_card_front_url":          profile.IdCardFrontUrl,
		"id_card_back_url":           profile.IdCardBackUrl,
		"bank_card_photo_url":        profile.BankCardPhotoUrl,
		"credit_code":                profile.CreditCode,
		"legal_person_name":          profile.LegalPersonName,
		"legal_person_phone":         profile.LegalPersonPhone,
		"bank_branch_code":           profile.BankBranchCode,
		"contact_person":             profile.ContactPerson,
		"business_license_url":       profile.BusinessLicenseUrl,
		"corporate_account_proof_url": profile.CorporateAccountProofUrl,
		"invoice_url":                profile.InvoiceUrl,
	}
}

func buildWithdrawalProfileJSON(req createDistributorWithdrawalRequest) (string, error) {
	accountType := req.AccountType
	if accountType == 0 {
		accountType = model.DistributorApplyTypePersonal
	}
	p := model.DistributorWithdrawalProfile{
		IdCardNo:                 strings.TrimSpace(req.IdCardNo),
		IdCardExpiry:             strings.TrimSpace(req.IdCardExpiry),
		Mobile:                   strings.TrimSpace(req.Mobile),
		BankReservedPhone:        strings.TrimSpace(req.BankReservedPhone),
		IdCardFrontUrl:           strings.TrimSpace(req.IdCardFrontUrl),
		IdCardBackUrl:            strings.TrimSpace(req.IdCardBackUrl),
		BankCardPhotoUrl:         strings.TrimSpace(req.BankCardPhotoUrl),
		CreditCode:               strings.TrimSpace(req.CreditCode),
		LegalPersonName:          strings.TrimSpace(req.LegalPersonName),
		LegalPersonPhone:         strings.TrimSpace(req.LegalPersonPhone),
		BankBranchCode:           strings.TrimSpace(req.BankBranchCode),
		ContactPerson:            strings.TrimSpace(req.ContactPerson),
		BusinessLicenseUrl:       strings.TrimSpace(req.BusinessLicenseUrl),
		CorporateAccountProofUrl: strings.TrimSpace(req.CorporateAccountProofUrl),
		InvoiceUrl:               strings.TrimSpace(req.InvoiceUrl),
	}
	b, err := common.Marshal(p)
	if err != nil {
		return "", err
	}
	return string(b), nil
}

type submitDistributorApplicationRequest struct {
	ApplyType         int      `json:"apply_type"`
	RealName          string   `json:"real_name"`
	IdCardNo          string   `json:"id_card_no"`
	QualificationUrls []string `json:"qualification_urls"`
	Contact           string   `json:"contact"`
}

// PostDistributorApplication 提交/重新提交分销商申请
func PostDistributorApplication(c *gin.Context) {
	userId := c.GetInt("id")
	var req submitDistributorApplicationRequest
	if err := common.DecodeJson(c.Request.Body, &req); err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "无效的请求"})
		return
	}
	if req.ApplyType == 0 {
		req.ApplyType = model.DistributorApplyTypePersonal
	}
	urlsJSON, err := common.Marshal(req.QualificationUrls)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "资料序列化失败"})
		return
	}
	err = model.UpsertDistributorApplication(userId, req.ApplyType, req.RealName, req.IdCardNo, string(urlsJSON), req.Contact)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"success": true, "message": ""})
}

// GetMyDistributorApplication 当前用户的申请状态
func GetMyDistributorApplication(c *gin.Context) {
	userId := c.GetInt("id")
	app, err := model.GetDistributorApplicationByUserId(userId)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"success": true, "message": "", "data": app})
}

// GetDistributorCenterInfo 分销商中心汇总（邀请短链、默认比例等）
func GetDistributorCenterInfo(c *gin.Context) {
	userId := c.GetInt("id")
	user, err := model.GetUserById(userId, true)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
		return
	}
	if !model.UserIsDistributor(user) {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "您不是分销商"})
		return
	}
	if user.AffCode == "" {
		user.AffCode = common.GetRandomString(4)
		if err := user.Update(false); err != nil {
			c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
			return
		}
	}
	bps := user.DistributorCommissionBps
	if bps <= 0 {
		bps = common.AffiliateDefaultCommissionBps
	}
	applyType, applicationRealName, err := model.GetDistributorWithdrawAccountType(userId)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"message": "",
		"data": gin.H{
			"aff_code":                   user.AffCode,
			"aff_quota":                  user.AffQuota,
			"aff_history_quota":          user.AffHistoryQuota,
			"aff_count":                  user.AffCount,
			"distributor_commission_bps": user.DistributorCommissionBps,
			"effective_commission_bps":   bps,
			"default_commission_bps":     common.AffiliateDefaultCommissionBps,
			"apply_type":                 applyType,
			"application_real_name":      applicationRealName,
		},
	})
}

// GetDistributorInviteeCommissionLogs 分销商查看某一被邀请用户的充值分成明细（按笔：入账额度、当时比例、收益额度）。
func GetDistributorInviteeCommissionLogs(c *gin.Context) {
	userId := c.GetInt("id")
	u, err := model.GetUserById(userId, false)
	if err != nil || !model.UserIsDistributor(u) {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "仅分销商可查看"})
		return
	}
	inviteeId, err := strconv.Atoi(c.Param("invitee_id"))
	if err != nil || inviteeId <= 0 {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "参数错误"})
		return
	}
	invitee, err := model.GetUserById(inviteeId, false)
	if err != nil || invitee.InviterId != userId {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "无权查看或用户不存在"})
		return
	}
	pageInfo := common.GetPageQuery(c)
	items, total, err := model.ListAffInviteCommissionLogs(userId, inviteeId, pageInfo)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
		return
	}
	pageInfo.SetTotal(int(total))
	pageInfo.SetItems(items)
	c.JSON(http.StatusOK, gin.H{"success": true, "message": "", "data": pageInfo})
}

// GetDistributorInviteeProfitShareLogs 分销商查看某一被邀请用户的利润分成明细（按次结算）。
func GetDistributorInviteeProfitShareLogs(c *gin.Context) {
	userId := c.GetInt("id")
	u, err := model.GetUserById(userId, false)
	if err != nil || !model.UserIsDistributor(u) {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "仅分销商可查看"})
		return
	}
	inviteeId, err := strconv.Atoi(c.Param("invitee_id"))
	if err != nil || inviteeId <= 0 {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "参数错误"})
		return
	}
	invitee, err := model.GetUserById(inviteeId, false)
	if err != nil || invitee.InviterId != userId {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "无权查看或用户不存在"})
		return
	}
	pageInfo := common.GetPageQuery(c)
	items, total, err := model.ListAffInviteProfitShareLogs(userId, inviteeId, pageInfo)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
		return
	}
	pageInfo.SetTotal(int(total))
	pageInfo.SetItems(items)
	c.JSON(http.StatusOK, gin.H{"success": true, "message": "", "data": pageInfo})
}

type rejectApplicationRequest struct {
	Reason string `json:"reason"`
}

// ListDistributorApplicationsAdmin 管理端：申请列表
func ListDistributorApplicationsAdmin(c *gin.Context) {
	pageInfo := common.GetPageQuery(c)
	status, _ := strconv.Atoi(c.Query("status"))
	applyType, _ := strconv.Atoi(c.Query("apply_type"))
	q := model.DistributorApplicationListQuery{
		Keyword:   c.Query("keyword"),
		Status:    status,
		ApplyType: applyType,
		DateFrom:  parseInt64Query(c.Query("date_from")),
		DateTo:    parseInt64Query(c.Query("date_to")),
		PageInfo:  pageInfo,
	}
	rows, usernames, total, err := model.ListDistributorApplicationsAdmin(q)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
		return
	}
	items := make([]gin.H, 0, len(rows))
	for i := range rows {
		items = append(items, gin.H{
			"id":                 rows[i].Id,
			"user_id":            rows[i].UserId,
			"username":           usernames[i],
			"apply_type":         rows[i].ApplyType,
			"real_name":          rows[i].RealName,
			"contact":            rows[i].Contact,
			"status":             rows[i].Status,
			"reject_reason":      rows[i].RejectReason,
			"created_at":         rows[i].CreatedAt,
			"id_card_no_mask":    maskIdCard(rows[i].IdCardNo),
			"qualification_urls": rows[i].QualificationUrls,
		})
	}
	pageInfo.SetTotal(int(total))
	pageInfo.SetItems(items)
	c.JSON(http.StatusOK, gin.H{"success": true, "message": "", "data": pageInfo})
}

func parseInt64Query(s string) int64 {
	s = strings.TrimSpace(s)
	if s == "" {
		return 0
	}
	n, _ := strconv.ParseInt(s, 10, 64)
	return n
}

func maskIdCard(id string) string {
	id = strings.TrimSpace(id)
	if len(id) <= 8 {
		return "****"
	}
	return id[:4] + strings.Repeat("*", len(id)-8) + id[len(id)-4:]
}

// GetDistributorApplicationAdmin 申请详情（管理员）
func GetDistributorApplicationAdmin(c *gin.Context) {
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil || id <= 0 {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "invalid id"})
		return
	}
	app, username, err := model.GetDistributorApplicationByIdAdmin(id)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"message": "",
		"data": gin.H{
			"id":                 app.Id,
			"user_id":            app.UserId,
			"username":           username,
			"apply_type":         app.ApplyType,
			"real_name":          app.RealName,
			"id_card_no":         app.IdCardNo,
			"qualification_urls": app.QualificationUrls,
			"contact":            app.Contact,
			"status":             app.Status,
			"reject_reason":      app.RejectReason,
			"reviewer_id":        app.ReviewerId,
			"reviewed_at":        app.ReviewedAt,
			"created_at":         app.CreatedAt,
			"updated_at":         app.UpdatedAt,
		},
	})
}

type approveDistributorApplicationRequest struct {
	DistributorCommissionBps *int `json:"distributor_commission_bps"`
}

// ApproveDistributorApplicationAdmin 通过申请（可选 body：distributor_commission_bps 万分之一，0=跟随系统）
func ApproveDistributorApplicationAdmin(c *gin.Context) {
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil || id <= 0 {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "invalid id"})
		return
	}
	var req approveDistributorApplicationRequest
	body, readErr := io.ReadAll(c.Request.Body)
	if readErr != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "读取请求失败"})
		return
	}
	if len(strings.TrimSpace(string(body))) > 0 {
		if err := common.Unmarshal(body, &req); err != nil {
			c.JSON(http.StatusOK, gin.H{"success": false, "message": "无效的请求"})
			return
		}
	}
	reviewerId := c.GetInt("id")
	if err := model.ApproveDistributorApplication(id, reviewerId, req.DistributorCommissionBps); err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
		return
	}
	if app, _, err := model.GetDistributorApplicationByIdAdmin(id); err == nil && app != nil {
		service.NotifyDistributorApplicationApproved(app.UserId)
	}
	c.JSON(http.StatusOK, gin.H{"success": true, "message": ""})
}

// RejectDistributorApplicationAdmin 驳回
func RejectDistributorApplicationAdmin(c *gin.Context) {
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil || id <= 0 {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "invalid id"})
		return
	}
	var req rejectApplicationRequest
	if err := common.DecodeJson(c.Request.Body, &req); err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "无效的请求"})
		return
	}
	reviewerId := c.GetInt("id")
	app, _, errApp := model.GetDistributorApplicationByIdAdmin(id)
	if err := model.RejectDistributorApplication(id, reviewerId, req.Reason); err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
		return
	}
	if errApp == nil && app != nil {
		service.NotifyDistributorApplicationRejected(app.UserId, req.Reason)
	}
	c.JSON(http.StatusOK, gin.H{"success": true, "message": ""})
}

// ListDistributorsAdmin 分销商人员列表
func ListDistributorsAdmin(c *gin.Context) {
	pageInfo := common.GetPageQuery(c)
	keyword := c.Query("keyword")
	applyType, _ := strconv.Atoi(c.Query("apply_type"))
	rows, total, err := model.ListDistributorsAdmin(model.DistributorListAdminQuery{
		Keyword:   keyword,
		ApplyType: applyType,
		PageInfo:  pageInfo,
	})
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
		return
	}
	items := make([]gin.H, 0, len(rows))
	for _, it := range rows {
		u := it.User
		bps := u.DistributorCommissionBps
		if bps <= 0 {
			bps = common.AffiliateDefaultCommissionBps
		}
		items = append(items, gin.H{
			"id":                         u.Id,
			"username":                   u.Username,
			"display_name":               u.DisplayName,
			"application_real_name":      it.ApplicationRealName,
			"application_apply_type":       it.ApplicationApplyType,
			"needs_supplement":           it.NeedsSupplement,
			"aff_code":                   u.AffCode,
			"aff_count":                  u.AffCount,
			"aff_quota":                  u.AffQuota,
			"aff_history_quota":          u.AffHistoryQuota,
			"distributor_commission_bps": u.DistributorCommissionBps,
			"effective_commission_bps":   bps,
		})
	}
	pageInfo.SetTotal(int(total))
	pageInfo.SetItems(items)
	c.JSON(http.StatusOK, gin.H{"success": true, "message": "", "data": pageInfo})
}

type putDistributorCommissionRequest struct {
	DistributorCommissionBps int `json:"distributor_commission_bps"`
}

// PutDistributorCommissionAdmin 设置单个分销商默认分成比例
func PutDistributorCommissionAdmin(c *gin.Context) {
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil || id <= 0 {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "invalid id"})
		return
	}
	var req putDistributorCommissionRequest
	if err := common.DecodeJson(c.Request.Body, &req); err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "无效的请求"})
		return
	}
	if err := model.SetUserDistributorCommissionBps(id, req.DistributorCommissionBps); err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"success": true, "message": ""})
}

// GetDistributorInviteesAdmin 某分销商名下邀请用户明细
func GetDistributorInviteesAdmin(c *gin.Context) {
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil || id <= 0 {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "invalid id"})
		return
	}
	u, err := model.GetUserById(id, false)
	if err != nil || !model.UserIsDistributor(u) {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "用户不是分销商"})
		return
	}
	pageInfo := common.GetPageQuery(c)
	keyword := strings.TrimSpace(c.Query("keyword"))
	if len(keyword) > 120 {
		keyword = keyword[:120]
	}
	items, total, err := model.ListAffInvitees(id, keyword, pageInfo)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
		return
	}
	pageInfo.SetTotal(int(total))
	pageInfo.SetItems(items)
	c.JSON(http.StatusOK, gin.H{"success": true, "message": "", "data": pageInfo})
}

// GetDistributorInviteeProfitSharesAdmin 管理端查看某分销商下某一被邀请用户的利润分成消费流水（分页）。
func GetDistributorInviteeProfitSharesAdmin(c *gin.Context) {
	distributorId, err := strconv.Atoi(c.Param("id"))
	if err != nil || distributorId <= 0 {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "invalid distributor id"})
		return
	}
	inviteeId, err := strconv.Atoi(c.Param("invitee_id"))
	if err != nil || inviteeId <= 0 {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "invalid invitee id"})
		return
	}
	if !common.IsDistributorProfitShareMode() {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "当前站点未启用利润分成模式"})
		return
	}
	dist, err := model.GetUserById(distributorId, false)
	if err != nil || !model.UserIsDistributor(dist) {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "用户不是分销商"})
		return
	}
	invitee, err := model.GetUserById(inviteeId, false)
	if err != nil || invitee == nil || invitee.InviterId != distributorId {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "该用户不是此分销商邀请的下级"})
		return
	}
	pageInfo := common.GetPageQuery(c)
	items, total, err := model.ListAffInviteProfitShareLogs(distributorId, inviteeId, pageInfo)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
		return
	}
	pageInfo.SetTotal(int(total))
	pageInfo.SetItems(items)
	c.JSON(http.StatusOK, gin.H{"success": true, "message": "", "data": pageInfo})
}

// PostDistributorWithdrawal 提交线下提现申请（暂扣 aff_quota）
func PostDistributorWithdrawal(c *gin.Context) {
	userId := c.GetInt("id")
	var req createDistributorWithdrawalRequest
	if err := common.DecodeJson(c.Request.Body, &req); err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "无效的请求"})
		return
	}
	accountType, _, err := model.GetDistributorWithdrawAccountType(userId)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
		return
	}
	profileJSON, err := buildWithdrawalProfileJSON(req)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
		return
	}
	quotaAmt := int(math.Round(req.QuotaAmount))
	if err := model.CreateDistributorWithdrawal(
		userId,
		accountType,
		strings.TrimSpace(req.RealName),
		strings.TrimSpace(req.BankName),
		strings.TrimSpace(req.BankAccount),
		profileJSON,
		"[]",
		strings.TrimSpace(req.WithdrawMonth),
		quotaAmt,
	); err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
		return
	}
	service.NotifyDistributorWithdrawalSubmitted(userId, quotaAmt)
	c.JSON(http.StatusOK, gin.H{"success": true, "message": ""})
}

// GetDistributorWithdrawals 当前用户提现记录
func GetDistributorWithdrawals(c *gin.Context) {
	userId := c.GetInt("id")
	pageInfo := common.GetPageQuery(c)
	rows, total, err := model.ListDistributorWithdrawals(userId, pageInfo)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
		return
	}
	items := make([]gin.H, 0, len(rows))
	for i := range rows {
		items = append(items, distributorWithdrawalToJSON(rows[i], ""))
	}
	pageInfo.SetTotal(int(total))
	pageInfo.SetItems(items)
	c.JSON(http.StatusOK, gin.H{"success": true, "message": "", "data": pageInfo})
}

// PostDistributorWithdrawalCancel 取消待审核提现，退回 aff_quota
func PostDistributorWithdrawalCancel(c *gin.Context) {
	userId := c.GetInt("id")
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil || id <= 0 {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "invalid id"})
		return
	}
	if err := model.CancelDistributorWithdrawal(userId, id); err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"success": true, "message": ""})
}

// ListDistributorWithdrawalsAdmin 管理端提现审核列表
func ListDistributorWithdrawalsAdmin(c *gin.Context) {
	pageInfo := common.GetPageQuery(c)
	status, _ := strconv.Atoi(c.Query("status"))
	accountType, _ := strconv.Atoi(c.Query("account_type"))
	keyword := c.Query("keyword")
	rows, total, err := model.ListDistributorWithdrawalsAdmin(status, accountType, keyword, pageInfo)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
		return
	}
	items := make([]gin.H, 0, len(rows))
	for i := range rows {
		items = append(items, distributorWithdrawalToJSON(rows[i].DistributorWithdrawal, rows[i].Username))
	}
	pageInfo.SetTotal(int(total))
	pageInfo.SetItems(items)
	c.JSON(http.StatusOK, gin.H{"success": true, "message": "", "data": pageInfo})
}

// ApproveDistributorWithdrawalAdmin 审核通过
func ApproveDistributorWithdrawalAdmin(c *gin.Context) {
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil || id <= 0 {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "invalid id"})
		return
	}
	reviewerId := c.GetInt("id")
	var wUserId int
	if w, err := model.GetDistributorWithdrawalByID(id); err == nil && w != nil {
		wUserId = w.UserId
	}
	if err := model.ApproveDistributorWithdrawalAdmin(id, reviewerId); err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
		return
	}
	if wUserId > 0 {
		service.NotifyDistributorWithdrawalApproved(wUserId)
	}
	c.JSON(http.StatusOK, gin.H{"success": true, "message": ""})
}

type rejectWithdrawalRequest struct {
	Reason string `json:"reason"`
}

// RejectDistributorWithdrawalAdmin 驳回并退回 aff_quota
func RejectDistributorWithdrawalAdmin(c *gin.Context) {
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil || id <= 0 {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "invalid id"})
		return
	}
	var req rejectWithdrawalRequest
	if err := common.DecodeJson(c.Request.Body, &req); err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "无效的请求"})
		return
	}
	reviewerId := c.GetInt("id")
	var wUserId int
	if w, err := model.GetDistributorWithdrawalByID(id); err == nil && w != nil {
		wUserId = w.UserId
	}
	if err := model.RejectDistributorWithdrawalAdmin(id, reviewerId, req.Reason); err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
		return
	}
	if wUserId > 0 {
		service.NotifyDistributorWithdrawalRejected(wUserId, req.Reason)
	}
	c.JSON(http.StatusOK, gin.H{"success": true, "message": ""})
}

// PostDistributorSettleAdmin 结账：清空该分销商待结算 aff_quota
func PostDistributorSettleAdmin(c *gin.Context) {
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil || id <= 0 {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "invalid id"})
		return
	}
	if err := model.AdminSettleDistributorAffQuota(id); err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"success": true, "message": ""})
}

type adminUpsertDistributorApplicationRequest struct {
	ApplyType         int      `json:"apply_type"`
	RealName          string   `json:"real_name"`
	IdCardNo          string   `json:"id_card_no"`
	QualificationUrls []string `json:"qualification_urls"`
	Contact           string   `json:"contact"`
}

// GetDistributorApplicationByUserAdmin 管理端：查看某分销商的申请/认证资料（手工开通可能无记录）
func GetDistributorApplicationByUserAdmin(c *gin.Context) {
	userId, err := strconv.Atoi(c.Param("id"))
	if err != nil || userId <= 0 {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "invalid id"})
		return
	}
	username, app, needsManualEntry, err := model.GetDistributorApplicationProfileByUserIdAdmin(userId)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
		return
	}
	data := gin.H{
		"user_id":            userId,
		"username":           username,
		"needs_manual_entry": needsManualEntry,
	}
	if app != nil {
		data["application"] = app
	} else {
		data["application"] = nil
	}
	c.JSON(http.StatusOK, gin.H{"success": true, "message": "", "data": data})
}

// PutDistributorApplicationByUserAdmin 管理端：补录或修改分销商申请资料
func PutDistributorApplicationByUserAdmin(c *gin.Context) {
	userId, err := strconv.Atoi(c.Param("id"))
	if err != nil || userId <= 0 {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "invalid id"})
		return
	}
	var req adminUpsertDistributorApplicationRequest
	if err := common.DecodeJson(c.Request.Body, &req); err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "无效的请求"})
		return
	}
	urls := make([]string, 0, len(req.QualificationUrls))
	for _, u := range req.QualificationUrls {
		u = strings.TrimSpace(u)
		if u != "" {
			urls = append(urls, u)
		}
	}
	urlsJSON, err := common.Marshal(urls)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": "资料序列化失败"})
		return
	}
	applyType := req.ApplyType
	if applyType == 0 {
		applyType = model.DistributorApplyTypePersonal
	}
	reviewerId := c.GetInt("id")
	if err := model.AdminUpsertDistributorApplicationByUser(userId, reviewerId, applyType, req.RealName, req.IdCardNo, string(urlsJSON), req.Contact); err != nil {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"success": true, "message": ""})
}
