package payment

import (
	"context"
	"gorm.io/gorm"
	"hfb_sys/backend/internal/model"
)

func (r *Repository) Query(ctx context.Context, userID uint64, orderID uint64) (*PaymentDTO, error) {
	var payment model.PaymentOrder
	if err := r.db.WithContext(ctx).Where("order_id = ? AND user_id = ? AND biz_type = ?", orderID, userID, "order_pay").Order("id DESC").First(&payment).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, ErrPaymentNotFound
		}
		return nil, err
	}
	if isOrderPaymentTerminalStatus(payment.Status) {
		dto := toDTO(payment)
		return &dto, nil
	}
	runtimeConfig, err := r.runtimeConfigForPayment(ctx, &payment)
	if err != nil {
		return nil, ErrPaymentUnavailable
	}
	if runtimeConfig.isMockMode() {
		dto := toDTO(payment)
		return &dto, nil
	}
	if runtimeConfig.Channel == nil {
		return nil, ErrPaymentUnavailable
	}
	resp, err := runtimeConfig.Channel.QueryPayment(ctx, payment.ThirdOrderID, payment.ProviderOrderID)
	if err != nil {
		return nil, err
	}
	if err := r.applyChannelStatus(ctx, &payment, resp.Status, resp.PayTime, resp.Raw, channelSourceQuery); err != nil {
		return nil, err
	}
	latest, err := r.findPaymentByID(ctx, payment.ID)
	if err != nil {
		return nil, err
	}
	dto := toDTO(*latest)
	return &dto, nil
}
func (r *Repository) AdminList(ctx context.Context, query AdminPaymentQuery) (*PaginatedResult, error) {
	db := r.db.WithContext(ctx).Table("payment_orders AS p").
		Select("p.*, COALESCE(u.phone, '') AS user_phone").
		Joins("LEFT JOIN users AS u ON u.id = p.user_id")
	countDB := r.db.WithContext(ctx).Model(&model.PaymentOrder{})
	if query.UserID > 0 {
		db = db.Where("p.user_id = ?", query.UserID)
		countDB = countDB.Where("user_id = ?", query.UserID)
	}
	if query.OrderID > 0 {
		db = db.Where("p.order_id = ?", query.OrderID)
		countDB = countDB.Where("order_id = ?", query.OrderID)
	}
	if query.OrderNo != "" {
		db = db.Where("p.order_no = ?", query.OrderNo)
		countDB = countDB.Where("order_no = ?", query.OrderNo)
	}
	if query.BizType != "" {
		db = db.Where("p.biz_type = ?", query.BizType)
		countDB = countDB.Where("biz_type = ?", query.BizType)
	}
	if query.Status != "" {
		db = db.Where("p.status = ?", query.Status)
		countDB = countDB.Where("status = ?", query.Status)
	}
	if query.Provider != "" {
		db = db.Where("p.provider = ?", query.Provider)
		countDB = countDB.Where("provider = ?", query.Provider)
	}
	var total int64
	if err := countDB.Count(&total).Error; err != nil {
		return nil, err
	}
	offset := (query.Page - 1) * query.PageSize
	var rows []adminPaymentRow
	if err := db.Order("p.id DESC").Offset(offset).Limit(query.PageSize).Scan(&rows).Error; err != nil {
		return nil, err
	}
	items := make([]AdminPaymentDTO, 0, len(rows))
	for _, row := range rows {
		items = append(items, row.toDTO())
	}
	return &PaginatedResult{Items: items, Total: total, Page: query.Page, PageSize: query.PageSize}, nil
}
func (r *Repository) findPaymentByID(ctx context.Context, paymentID uint64) (*model.PaymentOrder, error) {
	var payment model.PaymentOrder
	if err := r.db.WithContext(ctx).First(&payment, paymentID).Error; err != nil {
		return nil, err
	}
	return &payment, nil
}

type adminPaymentRow struct {
	model.PaymentOrder
	UserPhone string
}

func (row adminPaymentRow) toDTO() AdminPaymentDTO {
	errorCode, errorMessage := paymentErrorSummary(row.Status, row.RawResponse)
	return AdminPaymentDTO{
		ID:              row.ID,
		PaymentNo:       row.PaymentNo,
		OrderID:         row.OrderID,
		OrderNo:         row.OrderNo,
		UserID:          row.UserID,
		PaymentConfigID: row.PaymentConfigID,
		UserPhone:       row.UserPhone,
		Provider:        row.Provider,
		MerchantID:      row.MerchantID,
		ThirdOrderID:    row.ThirdOrderID,
		ProviderOrderID: row.ProviderOrderID,
		PayWay:          row.PayWay,
		AmountCent:      row.AmountCent,
		BizType:         row.BizType,
		Status:          row.Status,
		ErrorCode:       errorCode,
		ErrorMessage:    errorMessage,
		RawRequest:      row.RawRequest,
		RawResponse:     row.RawResponse,
		PaidAt:          row.PaidAt,
		NotifiedAt:      row.NotifiedAt,
		CreatedAt:       row.CreatedAt,
		UpdatedAt:       row.UpdatedAt,
	}
}
