package commission_withdrawal

import (
	"context"
	"encoding/json"
	"time"

	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/internal/model/dto"
	"github.com/break/junhong_cmp_fiber/internal/store"
	"github.com/break/junhong_cmp_fiber/internal/store/postgres"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
	"github.com/break/junhong_cmp_fiber/pkg/errors"
	"github.com/break/junhong_cmp_fiber/pkg/middleware"
	"gorm.io/gorm"
)

type Service struct {
	db                           *gorm.DB
	shopStore                    *postgres.ShopStore
	accountStore                 *postgres.AccountStore
	agentWalletStore             *postgres.AgentWalletStore
	agentWalletTransactionStore  *postgres.AgentWalletTransactionStore
	commissionWithdrawalReqStore *postgres.CommissionWithdrawalRequestStore
}

func New(
	db *gorm.DB,
	shopStore *postgres.ShopStore,
	accountStore *postgres.AccountStore,
	agentWalletStore *postgres.AgentWalletStore,
	agentWalletTransactionStore *postgres.AgentWalletTransactionStore,
	commissionWithdrawalReqStore *postgres.CommissionWithdrawalRequestStore,
) *Service {
	return &Service{
		db:                           db,
		shopStore:                    shopStore,
		accountStore:                 accountStore,
		agentWalletStore:             agentWalletStore,
		agentWalletTransactionStore:  agentWalletTransactionStore,
		commissionWithdrawalReqStore: commissionWithdrawalReqStore,
	}
}

func (s *Service) ListWithdrawalRequests(ctx context.Context, req *dto.WithdrawalRequestListReq) (*dto.WithdrawalRequestPageResult, error) {
	opts := &store.QueryOptions{
		Page:     req.Page,
		PageSize: req.PageSize,
		OrderBy:  "created_at DESC",
	}
	if opts.Page == 0 {
		opts.Page = 1
	}
	if opts.PageSize == 0 {
		opts.PageSize = constants.DefaultPageSize
	}

	filters := &postgres.WithdrawalRequestListFilters{
		WithdrawalNo: req.WithdrawalNo,
		Status:       req.Status,
	}

	if req.StartTime != "" {
		t, err := time.Parse("2006-01-02 15:04:05", req.StartTime)
		if err == nil {
			filters.StartTime = &t
		}
	}
	if req.EndTime != "" {
		t, err := time.Parse("2006-01-02 15:04:05", req.EndTime)
		if err == nil {
			filters.EndTime = &t
		}
	}

	requests, total, err := s.commissionWithdrawalReqStore.List(ctx, opts, filters)
	if err != nil {
		return nil, errors.Wrap(errors.CodeInternalError, err, "查询提现申请列表失败")
	}

	shopIDs := make([]uint, 0)
	applicantIDs := make([]uint, 0)
	processorIDs := make([]uint, 0)
	for _, r := range requests {
		if r.ShopID > 0 {
			shopIDs = append(shopIDs, r.ShopID)
		}
		if r.ApplicantID > 0 {
			applicantIDs = append(applicantIDs, r.ApplicantID)
		}
		if r.ProcessorID > 0 {
			processorIDs = append(processorIDs, r.ProcessorID)
		}
	}

	shopMap := make(map[uint]*model.Shop)
	for _, id := range shopIDs {
		shop, err := s.shopStore.GetByID(ctx, id)
		if err == nil {
			shopMap[id] = shop
		}
	}

	applicantMap := make(map[uint]string)
	processorMap := make(map[uint]string)
	if len(applicantIDs) > 0 {
		accounts, _ := s.accountStore.GetByIDs(ctx, applicantIDs)
		for _, acc := range accounts {
			applicantMap[acc.ID] = acc.Username
		}
	}
	if len(processorIDs) > 0 {
		accounts, _ := s.accountStore.GetByIDs(ctx, processorIDs)
		for _, acc := range accounts {
			processorMap[acc.ID] = acc.Username
		}
	}

	items := make([]dto.WithdrawalRequestItem, 0, len(requests))
	for _, r := range requests {
		shop := shopMap[r.ShopID]
		shopName := ""
		shopHierarchy := ""
		if shop != nil {
			shopName = shop.ShopName
			shopHierarchy = s.buildShopHierarchyPath(ctx, shop)
			if req.ShopName != "" && !containsSubstring(shopName, req.ShopName) {
				total--
				continue
			}
		}

		item := s.buildWithdrawalRequestItem(r, shopName, shopHierarchy, applicantMap, processorMap)
		items = append(items, item)
	}

	return &dto.WithdrawalRequestPageResult{
		Items: items,
		Total: total,
		Page:  opts.Page,
		Size:  opts.PageSize,
	}, nil
}

func (s *Service) Approve(ctx context.Context, id uint, req *dto.ApproveWithdrawalReq) (*dto.WithdrawalApprovalResp, error) {
	currentUserID := middleware.GetUserIDFromContext(ctx)
	if currentUserID == 0 {
		return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
	}

	withdrawal, err := s.commissionWithdrawalReqStore.GetByID(ctx, id)
	if err != nil {
		return nil, errors.New(errors.CodeNotFound, "提现申请不存在")
	}

	if withdrawal.Status != constants.WithdrawalStatusPending {
		return nil, errors.New(errors.CodeInvalidStatus, "申请状态不允许此操作")
	}

	// 获取店铺分佣钱包
	wallet, err := s.agentWalletStore.GetCommissionWallet(ctx, withdrawal.ShopID)
	if err != nil {
		return nil, errors.New(errors.CodeNotFound, "店铺佣金钱包不存在")
	}

	amount := withdrawal.Amount
	if req.Amount != nil {
		amount = *req.Amount
	}

	if wallet.FrozenBalance < amount {
		return nil, errors.New(errors.CodeInsufficientBalance, "钱包冻结余额不足")
	}

	now := time.Now()
	err = s.db.Transaction(func(tx *gorm.DB) error {
		// 从冻结余额扣款
		if err := s.agentWalletStore.DeductFrozenBalanceWithTx(ctx, tx, wallet.ID, amount); err != nil {
			return errors.Wrap(errors.CodeInternalError, err, "扣除冻结余额失败")
		}

		// 创建代理钱包交易记录
		refType := "withdrawal"
		refID := withdrawal.ID
		transaction := &model.AgentWalletTransaction{
			AgentWalletID:   wallet.ID,
			ShopID:          withdrawal.ShopID,
			UserID:          currentUserID,
			TransactionType: "withdrawal",
			Amount:          -amount,
			BalanceBefore:   wallet.Balance,
			BalanceAfter:    wallet.Balance,
			Status:          1,
			ReferenceType:   &refType,
			ReferenceID:     &refID,
			Creator:         currentUserID,
			ShopIDTag:       withdrawal.ShopID,
		}
		if err := s.agentWalletTransactionStore.CreateWithTx(ctx, tx, transaction); err != nil {
			return errors.Wrap(errors.CodeInternalError, err, "创建交易流水失败")
		}

		updates := map[string]interface{}{
			"status":       constants.WithdrawalStatusApproved,
			"processor_id": currentUserID,
			"processed_at": now,
			"payment_type": req.PaymentType,
			"remark":       req.Remark,
		}

		if req.Amount != nil {
			feeRate := withdrawal.FeeRate
			fee := amount * feeRate / 10000
			actualAmount := amount - fee
			updates["amount"] = amount
			updates["fee"] = fee
			updates["actual_amount"] = actualAmount
		}

		if req.WithdrawalMethod != nil {
			updates["withdrawal_method"] = *req.WithdrawalMethod
		}
		if req.AccountName != nil || req.AccountNumber != nil {
			accountInfo := make(map[string]interface{})
			if withdrawal.AccountInfo != nil {
				_ = json.Unmarshal(withdrawal.AccountInfo, &accountInfo)
			}
			if req.AccountName != nil {
				accountInfo["account_name"] = *req.AccountName
			}
			if req.AccountNumber != nil {
				accountInfo["account_number"] = *req.AccountNumber
			}
			infoBytes, _ := json.Marshal(accountInfo)
			updates["account_info"] = infoBytes
		}

		if err := s.commissionWithdrawalReqStore.UpdateStatusWithTx(ctx, tx, id, updates); err != nil {
			return errors.Wrap(errors.CodeInternalError, err, "更新提现申请状态失败")
		}

		return nil
	})

	if err != nil {
		return nil, err
	}

	return &dto.WithdrawalApprovalResp{
		ID:           withdrawal.ID,
		WithdrawalNo: withdrawal.WithdrawalNo,
		Status:       constants.WithdrawalStatusApproved,
		StatusName:   "已通过",
		ProcessedAt:  now.Format("2006-01-02 15:04:05"),
	}, nil
}

func (s *Service) Reject(ctx context.Context, id uint, req *dto.RejectWithdrawalReq) (*dto.WithdrawalApprovalResp, error) {
	currentUserID := middleware.GetUserIDFromContext(ctx)
	if currentUserID == 0 {
		return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
	}

	withdrawal, err := s.commissionWithdrawalReqStore.GetByID(ctx, id)
	if err != nil {
		return nil, errors.New(errors.CodeNotFound, "提现申请不存在")
	}

	if withdrawal.Status != constants.WithdrawalStatusPending {
		return nil, errors.New(errors.CodeInvalidStatus, "申请状态不允许此操作")
	}

	wallet, err := s.agentWalletStore.GetCommissionWallet(ctx, withdrawal.ShopID)
	if err != nil {
		return nil, errors.New(errors.CodeNotFound, "店铺佣金钱包不存在")
	}

	now := time.Now()
	err = s.db.Transaction(func(tx *gorm.DB) error {
		if err := s.agentWalletStore.UnfreezeBalanceWithTx(ctx, tx, wallet.ID, withdrawal.Amount); err != nil {
			return errors.Wrap(errors.CodeInternalError, err, "解冻余额失败")
		}

		refType := "withdrawal"
		refID := withdrawal.ID
		transaction := &model.AgentWalletTransaction{
			AgentWalletID:   wallet.ID,
			ShopID:          withdrawal.ShopID,
			UserID:          currentUserID,
			TransactionType: "refund",
			Amount:          withdrawal.Amount,
			BalanceBefore:   wallet.Balance,
			BalanceAfter:    wallet.Balance + withdrawal.Amount,
			Status:          1,
			ReferenceType:   &refType,
			ReferenceID:     &refID,
			Creator:         currentUserID,
			ShopIDTag:       withdrawal.ShopID,
		}
		if err := s.agentWalletTransactionStore.CreateWithTx(ctx, tx, transaction); err != nil {
			return errors.Wrap(errors.CodeInternalError, err, "创建交易流水失败")
		}

		updates := map[string]interface{}{
			"status":        constants.WithdrawalStatusRejected,
			"processor_id":  currentUserID,
			"processed_at":  now,
			"reject_reason": req.Remark,
			"remark":        req.Remark,
		}
		if err := s.commissionWithdrawalReqStore.UpdateStatusWithTx(ctx, tx, id, updates); err != nil {
			return errors.Wrap(errors.CodeInternalError, err, "更新提现申请状态失败")
		}

		return nil
	})

	if err != nil {
		return nil, err
	}

	return &dto.WithdrawalApprovalResp{
		ID:           withdrawal.ID,
		WithdrawalNo: withdrawal.WithdrawalNo,
		Status:       constants.WithdrawalStatusRejected,
		StatusName:   "已拒绝",
		ProcessedAt:  now.Format("2006-01-02 15:04:05"),
	}, nil
}

func (s *Service) buildShopHierarchyPath(ctx context.Context, shop *model.Shop) string {
	if shop == nil {
		return ""
	}
	path := shop.ShopName
	current := shop
	depth := 0
	for current.ParentID != nil && depth < 2 {
		parent, err := s.shopStore.GetByID(ctx, *current.ParentID)
		if err != nil {
			break
		}
		path = parent.ShopName + "_" + path
		current = parent
		depth++
	}
	return path
}

func (s *Service) buildWithdrawalRequestItem(r *model.CommissionWithdrawalRequest, shopName, shopHierarchy string, applicantMap, processorMap map[uint]string) dto.WithdrawalRequestItem {
	var processorID *uint
	if r.ProcessorID > 0 {
		processorID = &r.ProcessorID
	}

	var accountName, accountNumber, bankName string
	if len(r.AccountInfo) > 0 {
		var info map[string]interface{}
		if err := json.Unmarshal(r.AccountInfo, &info); err == nil {
			if v, ok := info["account_name"].(string); ok {
				accountName = v
			}
			if v, ok := info["account_number"].(string); ok {
				accountNumber = v
			}
			if v, ok := info["bank_name"].(string); ok {
				bankName = v
			}
		}
	}

	var processedAt string
	if r.ProcessedAt != nil {
		processedAt = r.ProcessedAt.Format("2006-01-02 15:04:05")
	}

	return dto.WithdrawalRequestItem{
		ID:               r.ID,
		WithdrawalNo:     r.WithdrawalNo,
		Amount:           r.Amount,
		FeeRate:          r.FeeRate,
		Fee:              r.Fee,
		ActualAmount:     r.ActualAmount,
		Status:           r.Status,
		StatusName:       constants.GetWithdrawalStatusName(r.Status),
		ShopID:           r.ShopID,
		ShopName:         shopName,
		ShopHierarchy:    shopHierarchy,
		ApplicantID:      r.ApplicantID,
		ApplicantName:    applicantMap[r.ApplicantID],
		ProcessorID:      processorID,
		ProcessorName:    processorMap[r.ProcessorID],
		WithdrawalMethod: r.WithdrawalMethod,
		PaymentType:      r.PaymentType,
		AccountName:      accountName,
		AccountNumber:    accountNumber,
		BankName:         bankName,
		RejectReason:     r.RejectReason,
		Remark:           r.Remark,
		CreatedAt:        r.CreatedAt.Format("2006-01-02 15:04:05"),
		ProcessedAt:      processedAt,
	}
}

func containsSubstring(s, substr string) bool {
	for i := 0; i <= len(s)-len(substr); i++ {
		if s[i:i+len(substr)] == substr {
			return true
		}
	}
	return false
}
