package agent

import (
	"context"
	"database/sql"
	"fmt"
	"time"
	"tydata-server/app/main/model"
	"tydata-server/common/ctxdata"
	"tydata-server/common/xerr"
	"tydata-server/pkg/lzkit/lzUtils"

	"github.com/cenkalti/backoff/v4"
	"github.com/pkg/errors"
	"github.com/smartwalle/alipay/v3"
	"github.com/zeromicro/go-zero/core/stores/sqlx"

	"tydata-server/app/main/api/internal/svc"
	"tydata-server/app/main/api/internal/types"

	"github.com/zeromicro/go-zero/core/logx"
)

// 状态常量
const (
	StatusProcessing = 1 // 处理中
	StatusSuccess    = 2 // 成功
	StatusFailed     = 3 // 失败
)

// 前端响应状态
const (
	WithdrawStatusProcessing = 1
	WithdrawStatusSuccess    = 2
	WithdrawStatusFailed     = 3
)

type AgentWithdrawalLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewAgentWithdrawalLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AgentWithdrawalLogic {
	return &AgentWithdrawalLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *AgentWithdrawalLogic) AgentWithdrawal(req *types.WithdrawalReq) (*types.WithdrawalResp, error) {
	var (
		outBizNo    string
		withdrawRes = &types.WithdrawalResp{}
		agentID     int64
	)
	var finalWithdrawAmount float64 // 实际到账金额
	// 使用事务处理核心操作
	err := l.svcCtx.AgentModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
		userID, err := ctxdata.GetUidFromCtx(l.ctx)
		if err != nil {
			return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户ID失败: %v", err)
		}

		// 查询代理信息
		agentModel, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
		if err != nil {
			return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败: %v", err)
		}
		agentID = agentModel.Id // 保存agentId用于日志
		agentRealName, err := l.svcCtx.AgentRealNameModel.FindOneByAgentId(l.ctx, agentModel.Id)
		if err != nil {
			if errors.Is(err, model.ErrNotFound) {
				return errors.Wrapf(xerr.NewErrMsg("您未进行实名认证, 无法提现"), "您未进行实名认证")
			}
			return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询代理实名信息失败: %v", err)
		}
		if agentRealName.Status != model.AgentRealNameStatusApproved {
			return errors.Wrapf(xerr.NewErrMsg("您的实名认证未通过, 无法提现"), "您的实名认证未通过")
		}
		if agentRealName.Name != req.PayeeName {
			return errors.Wrapf(xerr.NewErrMsg("您的实名认证信息不匹配, 无法提现"), "您的实名认证信息不匹配")
		}
		// 查询钱包
		agentWallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(l.ctx, agentModel.Id)
		if err != nil {
			return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理钱包失败: %v", err)
		}

		// 校验可提现金额
		if req.Amount > agentWallet.Balance {
			return errors.Wrapf(xerr.NewErrMsg("您可提现的余额不足"), "获取用户ID失败")
		}

		// 生成交易号
		outBizNo = "W_" + l.svcCtx.AlipayService.GenerateOutTradeNo()

		// 冻结资金（事务内操作）
		if err = l.freezeFunds(session, agentWallet, req.Amount); err != nil {
			return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "资金冻结失败: %v", err)
		}
		yearMonth := int64(time.Now().Year()*100 + int(time.Now().Month()))
		// 统一按6%收取税收，不再有免税额度
		taxRate := l.svcCtx.Config.TaxConfig.TaxRate
		var (
			taxAmount        float64 // 应缴税费
			taxDeductionPart float64 // 应税金额
			TaxStatus        int64   // 扣税状态
			exemptionAmount  float64 // 免税金额（固定为0）
		)

		// 统一扣税逻辑：所有提现都按6%收取税收
		exemptionAmount = 0                          // 免税金额 = 0
		TaxStatus = model.TaxStatusPending           // 扣税状态 = 待扣税
		taxDeductionPart = req.Amount                // 应税金额 = 提现金额
		taxAmount = taxDeductionPart * taxRate       // 应缴税费 = 应税金额 * 税率
		finalWithdrawAmount = req.Amount - taxAmount // 实际到账金额 = 提现金额 - 应缴税费

		// 创建提现记录（初始状态为处理中）
		withdrawalID, err := l.createWithdrawalRecord(session, agentModel.Id, req.PayeeAccount, req.PayeeName, req.Amount, finalWithdrawAmount, taxAmount, outBizNo)
		if err != nil {
			return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建提现记录失败: %v", err)
		}
		// 扣税记录
		taxModel := &model.AgentWithdrawalTax{
			AgentId:           agentModel.Id,
			YearMonth:         yearMonth,
			WithdrawalId:      withdrawalID,
			WithdrawalAmount:  req.Amount,
			ExemptionAmount:   exemptionAmount,
			TaxableAmount:     taxDeductionPart,
			TaxRate:           taxRate,
			TaxAmount:         taxAmount,
			ActualAmount:      finalWithdrawAmount,
			TaxStatus:         TaxStatus,
			Remark:            sql.NullString{String: "提现成功自动扣税", Valid: true},
			ExemptionRecordId: 0, // 不再使用免税额度记录
		}
		_, err = l.svcCtx.AgentWithdrawalTaxModel.Insert(ctx, session, taxModel)
		if err != nil {
			return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建扣税记录失败: %v", err)
		}
		return nil
	})

	if err != nil {
		return nil, err
	}

	// 支付宝提现不再直接调用转账接口，改为先申请后审核
	// 直接返回申请中状态，等待管理员审核
	withdrawRes.Status = WithdrawStatusProcessing
	withdrawRes.FailMsg = ""

	l.Logger.Infof("支付宝提现申请成功 outBizNo:%s agentId:%d amount:%f", outBizNo, agentID, req.Amount)
	return withdrawRes, nil
}

// 错误类型映射
func (l *AgentWithdrawalLogic) mapAlipayError(code string) string {
	errorMapping := map[string]string{
		// 账户存在性错误
		"PAYEE_ACCOUNT_NOT_EXSIT": "收款账户不存在，请检查账号是否正确",
		"PAYEE_NOT_EXIST":         "收款账户不存在或姓名有误，请核实信息",
		"PAYEE_ACC_OCUPIED":       "收款账号存在多个账户，无法确认唯一性",
		"PAYEE_MID_CANNOT_SAME":   "收款方和中间方不能是同一个人，请修改收款方或者中间方信息",

		// 实名认证问题
		"PAYEE_CERTIFY_LEVEL_LIMIT": "收款方未完成实名认证",
		"PAYEE_NOT_RELNAME_CERTIFY": "收款方未完成实名认证",
		"PAYEE_CERT_INFO_ERROR":     "收款方证件信息不匹配",

		// 账户状态异常
		"PAYEE_ACCOUNT_STATUS_ERROR":       "收款账户状态异常，请更换账号",
		"PAYEE_USERINFO_STATUS_ERROR":      "收款账户状态异常，无法收款",
		"PERMIT_LIMIT_PAYEE":               "收款账户异常，请更换账号",
		"BLOCK_USER_FORBBIDEN_RECIEVE":     "账户冻结无法收款",
		"PAYEE_TRUSTEESHIP_ACC_OVER_LIMIT": "收款方托管子户累计收款金额超限",

		// 账户信息错误
		"PAYEE_USERINFO_ERROR":     "收款方姓名或信息不匹配",
		"PAYEE_CARD_INFO_ERROR":    "收款支付宝账号及户名不一致",
		"PAYEE_IDENTITY_NOT_MATCH": "收款方身份信息不匹配",
		"PAYEE_USER_IS_INST":       "收款方为金融机构，不能使用提现功能，请更换收款账号",
		"PAYEE_USER_TYPE_ERROR":    "该支付宝账号类型不支持提现，请更换收款账号",

		// 权限与限制
		"PAYEE_RECEIVE_COUNT_EXCEED_LIMIT":  "收款次数超限，请明日再试",
		"PAYEE_OUT_PERMLIMIT_CHECK_FAILURE": "收款方权限校验不通过",
		"PERMIT_NON_BANK_LIMIT_PAYEE":       "收款方未完善身份信息，无法收款",
	}
	if msg, ok := errorMapping[code]; ok {
		return msg
	}
	return "系统错误，请联系客服"
}

// 创建提现记录（事务内操作）
func (l *AgentWithdrawalLogic) createWithdrawalRecord(session sqlx.Session, agentID int64, payeeAccount string, payeeName string, amount float64, finalWithdrawAmount float64, taxAmount float64, outBizNo string) (int64, error) {
	record := &model.AgentWithdrawal{
		AgentId:      agentID,
		WithdrawType: 1, // 支付宝提现
		WithdrawNo:   outBizNo,
		PayeeAccount: payeeAccount,
		PayeeName:    sql.NullString{String: payeeName, Valid: true}, // 设置收款人姓名
		Amount:       amount,
		ActualAmount: finalWithdrawAmount,
		TaxAmount:    taxAmount,
		Status:       StatusProcessing,
	}

	result, err := l.svcCtx.AgentWithdrawalModel.Insert(l.ctx, session, record)
	if err != nil {
		return 0, err
	}
	return result.LastInsertId()
}

// 冻结资金（事务内操作）
func (l *AgentWithdrawalLogic) freezeFunds(session sqlx.Session, wallet *model.AgentWallet, amount float64) error {
	wallet.Balance -= amount
	wallet.FrozenBalance += amount
	err := l.svcCtx.AgentWalletModel.UpdateWithVersion(l.ctx, session, wallet)
	if err != nil {
		return err
	}

	return nil
}

// 处理异步轮询
func (l *AgentWithdrawalLogic) startAsyncPolling(outBizNo string) {
	go func() {
		detachedCtx := context.WithoutCancel(l.ctx)
		retryConfig := &backoff.ExponentialBackOff{
			InitialInterval:     10 * time.Second,
			RandomizationFactor: 0.5, // 增加随机因子防止惊群
			Multiplier:          2,
			MaxInterval:         30 * time.Second,
			MaxElapsedTime:      5 * time.Minute, // 缩短总超时
			Clock:               backoff.SystemClock,
		}
		retryConfig.Reset()
		operation := func() error {
			statusRsp, err := l.svcCtx.AlipayService.QueryTransferStatus(detachedCtx, outBizNo)
			if err != nil {
				return err // 触发重试
			}

			switch statusRsp.Status {
			case "SUCCESS":
				l.handleTransferSuccess(outBizNo, statusRsp)
				return nil
			case "FAIL":
				l.handleTransferFailure(outBizNo, statusRsp)
				return nil
			default:
				return fmt.Errorf("转账处理中")
			}
		}

		err := backoff.RetryNotify(operation,
			backoff.WithContext(retryConfig, detachedCtx),
			func(err error, duration time.Duration) {
				l.Logger.Infof("轮询延迟 outBizNo:%s 等待:%v", outBizNo, duration)
			})

		if err != nil {
			l.handleTransferTimeout(outBizNo)
		}
	}()
}

// 统一状态更新
func (l *AgentWithdrawalLogic) updateWithdrawalStatus(outBizNo string, status int64, errorMsg string) {
	detachedCtx := context.WithoutCancel(l.ctx)

	err := l.svcCtx.AgentModel.Trans(detachedCtx, func(ctx context.Context, session sqlx.Session) error {
		// 获取提现记录
		record, err := l.svcCtx.AgentWithdrawalModel.FindOneByWithdrawNo(l.ctx, outBizNo)
		if err != nil {
			return err
		}

		// 更新状态
		record.Status = status
		record.Remark = lzUtils.StringToNullString(errorMsg)
		if _, err = l.svcCtx.AgentWithdrawalModel.Update(ctx, session, record); err != nil {
			return err
		}
		// 失败时解冻资金
		if status == StatusFailed {
			wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(ctx, record.AgentId)
			if err != nil {
				return err
			}

			wallet.Balance += record.Amount
			wallet.FrozenBalance -= record.Amount
			if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, wallet); err != nil {
				return err
			}
			taxModel, err := l.svcCtx.AgentWithdrawalTaxModel.FindOneByWithdrawalId(ctx, record.Id)
			if err != nil {
				return err
			}
			if taxModel.TaxStatus == model.TaxStatusPending {
				taxModel.TaxStatus = model.TaxStatusFailed // 扣税状态 = 失败
				taxModel.TaxTime = sql.NullTime{Time: time.Now(), Valid: true}
				if err := l.svcCtx.AgentWithdrawalTaxModel.UpdateWithVersion(ctx, session, taxModel); err != nil {
					return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新扣税记录失败: %v", err)
				}
			}
			// 不再需要恢复免税额度，因为统一按6%收取税收
		}
		if status == StatusSuccess {
			wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(ctx, record.AgentId)
			if err != nil {
				return err
			}

			wallet.FrozenBalance -= record.Amount
			if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, wallet); err != nil {
				return err
			}
			taxModel, err := l.svcCtx.AgentWithdrawalTaxModel.FindOneByWithdrawalId(ctx, record.Id)
			if err != nil {
				return err
			}
			if taxModel.TaxStatus == model.TaxStatusPending {
				taxModel.TaxStatus = model.TaxStatusSuccess // 扣税状态 = 成功
				taxModel.TaxTime = sql.NullTime{Time: time.Now(), Valid: true}
				if err := l.svcCtx.AgentWithdrawalTaxModel.UpdateWithVersion(ctx, session, taxModel); err != nil {
					return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新扣税记录失败: %v", err)
				}
			}

			// 提现成功后，给上级代理发放提现奖励
			withdrawRewardErr := l.svcCtx.AgentService.GiveWithdrawReward(ctx, record.AgentId, record.Amount, session)
			if withdrawRewardErr != nil {
				l.Logger.Errorf("发放提现奖励失败，代理ID：%d，提现金额：%f，错误：%+v", record.AgentId, record.Amount, withdrawRewardErr)
				// 提现奖励失败不影响主流程，只记录日志
			} else {
				l.Logger.Infof("发放提现奖励成功，代理ID：%d，提现金额：%f", record.AgentId, record.Amount)
			}
		}
		return nil
	})

	if err != nil {
		l.Logger.Errorf("状态更新失败 outBizNo:%s error:%v", outBizNo, err)
	}
}

// 成功处理
func (l *AgentWithdrawalLogic) handleTransferSuccess(outBizNo string, rsp interface{}) {
	l.updateWithdrawalStatus(outBizNo, StatusSuccess, "")
	l.Logger.Infof("提现成功 outBizNo:%s", outBizNo)
}

// 失败处理
func (l *AgentWithdrawalLogic) handleTransferFailure(outBizNo string, rsp interface{}) {
	var errorMsg string
	if resp, ok := rsp.(*alipay.FundTransUniTransferRsp); ok {
		errorMsg = l.mapAlipayError(resp.SubCode)
	}
	l.updateWithdrawalStatus(outBizNo, StatusFailed, errorMsg)
	l.Logger.Errorf("提现失败 outBizNo:%s reason:%s", outBizNo, errorMsg)
}

// 超时处理
func (l *AgentWithdrawalLogic) handleTransferTimeout(outBizNo string) {
	l.updateWithdrawalStatus(outBizNo, StatusFailed, "系统处理超时")
	l.Logger.Errorf("轮询超时 outBizNo:%s", outBizNo)
}

// 错误处理
func (l *AgentWithdrawalLogic) handleTransferError(outBizNo string, err error, contextMsg string) {
	l.updateWithdrawalStatus(outBizNo, StatusFailed, "系统处理异常")
	l.Logger.Errorf("%s outBizNo:%s error:%v", contextMsg, outBizNo, err)
}

func (l *AgentWithdrawalLogic) createMonthlyExemption(session sqlx.Session, agentId int64, yearMonth int64) (*model.AgentWithdrawalTaxExemption, error) {
	exemption := &model.AgentWithdrawalTaxExemption{
		AgentId:                  agentId,
		YearMonth:                yearMonth,
		TotalExemptionAmount:     l.svcCtx.Config.TaxConfig.TaxExemptionAmount,
		UsedExemptionAmount:      0.00,
		RemainingExemptionAmount: l.svcCtx.Config.TaxConfig.TaxExemptionAmount,
	}

	result, err := l.svcCtx.AgentWithdrawalTaxExemptionModel.Insert(l.ctx, session, exemption)
	if err != nil {
		return nil, err
	}

	id, _ := result.LastInsertId()
	exemption.Id = id
	return exemption, nil
}
