package repositories

import (
	config "astrology-api/configs"
	"astrology-api/constants"
	"astrology-api/dto"
	models "astrology-api/models/usermodel"
	"strings"

	"gorm.io/gorm"
)

type WalletRepository interface {
	GetWalletTransactions(userID uint, page int, limit int) ([]dto.WalletTransactionResponse, int64, error)

	GetWalletSummary(userID uint) (*dto.WalletSummaryResponse, error)

	GetRechargeTransactions(userID uint) ([]dto.WalletTransactionResponse, error)

	GetChatTransactions(userID uint) ([]dto.WalletTransactionResponse, error)

	GetCallTransactions(userID uint) ([]dto.WalletTransactionResponse, error)

	GetOrderTransactions(userID uint) ([]dto.WalletTransactionResponse, error)

	GetWalletTransactionDetail(userID uint, id uint, transactionType string) (*dto.WalletTransactionDetail, error)

	GetRechargeOffers() ([]models.RechargeAmount, error)

	CreatePayment(payment *models.Payment) error

	UpdatePayment(payment *models.Payment) error

	FindPaymentByTransactionID(transactionID string) (*models.Payment, error)

	FindPaymentByOrderID(orderID string) (*models.Payment, error)

	GetPaymentByTransactionID(transactionID string) (*models.Payment, error)

	GetUserWallet(userID uint) (*models.UserWallet, error)

	UpdateUserWallet(wallet *models.UserWallet) error

	CreateWalletTransaction(transaction *models.WalletTransaction) error
}

type walletRepository struct {
	db *gorm.DB
}

func NewWalletRepository(db *gorm.DB) WalletRepository {
	return &walletRepository{
		db: db,
	}
}

func (r *walletRepository) GetWalletTransactions(userID uint, page int, limit int) ([]dto.WalletTransactionResponse, int64, error) {

	var records []dto.WalletTransactionResponse
	var total int64

	offset := (page - 1) * limit

	query := r.db.
		Table("wallettransaction wt").
		Where("wt.userId = ?", userID)

	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	err := query.
		Select(` wt.id, wt.amount, wt.transactionType, wt.isCredit, wt.created_at`).
		Order("wt.created_at DESC").
		Limit(limit).
		Offset(offset).
		Scan(&records).Error

	if err != nil {
		return nil, 0, err
	}

	return records, total, nil
}

func (r *walletRepository) GetWalletSummary(userID uint) (*dto.WalletSummaryResponse, error) {

	var summary dto.WalletSummaryResponse

	var credit float64
	var debit float64
	var total int64
	var available_balance float64

	wallet, err := r.GetUserWallet(userID)
	if err != nil {
		return nil, err
	}
	available_balance = *wallet.Amount

	r.db.Table("wallettransaction").
		Where("userId = ?", userID).
		Where("isCredit = ?", true).
		Select("IFNULL(SUM(amount),0)").
		Scan(&credit)

	r.db.Table("wallettransaction").
		Where("userId = ?", userID).
		Where("isCredit = ?", false).
		Select("IFNULL(SUM(amount),0)").
		Scan(&debit)

	r.db.Table("wallettransaction").
		Where("userId = ?", userID).
		Count(&total)

	summary.CreditBalance = credit
	summary.DebitBalance = debit

	summary.WalletBalance = available_balance
	summary.TotalTransactions = total
	summary.Status = 200

	return &summary, nil
}

func (r *walletRepository) GetRechargeTransactions(
	userID uint,
) ([]dto.WalletTransactionResponse, error) {

	var records []dto.WalletTransactionResponse

	// Every wallettransaction row except consultation billing, which is
	// already listed from user_chat_histories / user_call_histories.
	err := r.db.
		Table("wallettransaction wt").
		Select(`wt.id,
			CASE WHEN UPPER(wt.transactionType) = 'RECHARGE' THEN 'Wallet Recharge'
				ELSE REPLACE(COALESCE(wt.transactionType, 'Wallet Transaction'), '_', ' ') END AS title,
			CONCAT('WT', wt.id) AS transactionNumber,
			CASE WHEN UPPER(wt.transactionType) = 'RECHARGE' THEN 'RECHARGE'
				ELSE COALESCE(wt.transactionType, '') END AS transactionType,
			wt.amount, COALESCE(wt.isCredit, 0) AS isCredit,
			CASE WHEN UPPER(wt.transactionType) = 'RECHARGE' THEN 'wallet'
				WHEN wt.isCredit = 1 THEN 'credit' ELSE 'debit' END AS icon,
			wt.created_at AS createdAt`).
		Where("wt.userId = ?", userID).
		Where("COALESCE(wt.transactionType, '') NOT IN ?", []string{
			constants.TxnTypeChatConsultation,
			constants.TxnTypeAudioConsultation,
			constants.TxnTypeVideoConsultation,
		}).
		Order("wt.created_at DESC").
		Scan(&records).Error

	return records, err
}

func (r *walletRepository) GetChatTransactions(
	userID uint,
) ([]dto.WalletTransactionResponse, error) {

	var records []dto.WalletTransactionResponse

	err := r.db.
		Table("user_chat_histories uch").
		Joins("LEFT JOIN astrologers a ON a.id = uch.astrologerId").
		Select(`
			uch.id,
			CONCAT('Chat with ',a.name) AS title,
			uch.chatHistoryNumber AS transactionNumber,
			'CHAT' AS transactionType,
			uch.deductionAmount AS amount,
			0 AS isCredit,
			'chat' AS icon,
			uch.created_at AS createdAt,
			a.name AS astrologerName
		`).
		Where("uch.userId = ?", userID).
		Order("uch.created_at DESC").
		Scan(&records).Error

	return records, err
}

func (r *walletRepository) GetCallTransactions(
	userID uint,
) ([]dto.WalletTransactionResponse, error) {

	var records []dto.WalletTransactionResponse

	err := r.db.
		Table("user_call_histories uch").
		Joins("LEFT JOIN astrologers a ON a.id = uch.astrologerId").
		Select(`uch.id, CONCAT('Call with ',a.name) AS title,
			uch.callHistoryNumber AS transactionNumber,
			'CALL' AS transactionType,
			uch.deductionAmount AS amount,
			0 AS isCredit,
			'call' AS icon,
			uch.created_at AS createdAt,
			a.name AS astrologerName
		`).Where("uch.userId = ?", userID).
		Order("uch.created_at DESC").
		Scan(&records).Error

	return records, err
}

func (r *walletRepository) GetOrderTransactions(
	userID uint,
) ([]dto.WalletTransactionResponse, error) {

	var records []dto.WalletTransactionResponse

	err := r.db.
		Table("user_orders uo").
		Joins("LEFT JOIN astromall_products ap ON ap.id = uo.astromallProductId").
		Select(`
			uo.id,
			CONCAT('Buy ',ap.name) AS title,
			CONCAT('ORD',uo.id) AS transactionNumber,
			'ORDER' AS transactionType,
			ap.amount,
			0 AS isCredit,
			'shopping' AS icon,
			uo.created_at AS createdAt,
			ap.name AS productName
		`).
		Where("uo.userId = ?", userID).
		Order("uo.created_at DESC").
		Scan(&records).Error

	return records, err
}

// transactionDetailRow is the flat shape every detail query scans into;
// the columns a source does not have come back as their zero value.
type transactionDetailRow struct {
	ID                uint
	Title             string
	TransactionNumber string
	TransactionType   string
	Amount            float64
	IsCredit          bool
	Status            string
	Icon              string
	CreatedAt         string

	AstrologerID           uint
	AstrologerName         string
	AstrologerProfileImage string

	SessionType     string
	DurationSeconds int

	PaymentMode      string
	PaymentReference string
	OrderID          string
	PaymentStatus    string
	CashbackAmount   float64
	HasPayment       bool

	ProductID           uint
	ProductName         string
	ProductImage        string
	ProductPrice        float64
	ProductSellingPrice float64

	AddressName     string
	AddressPhone    string
	AddressFlatNo   string
	AddressLocality string
	AddressLandmark string
	AddressCity     string
	AddressState    string
	AddressCountry  string
	AddressPincode  string
	HasAddress      bool
}

const astrologerDetailColumns = `
	COALESCE(a.id, 0) AS astrologer_id,
	COALESCE(NULLIF(a.displayName, ''), a.name, '') AS astrologer_name,
	COALESCE(a.profileImage, '') AS astrologer_profile_image`

// GetWalletTransactionDetail resolves one row of the /wallet/transactions list.
// transactionType picks the source table the same way the list built it, and
// every query is scoped to userID so another account's id is simply not found.
func (r *walletRepository) GetWalletTransactionDetail(
	userID uint,
	id uint,
	transactionType string,
) (*dto.WalletTransactionDetail, error) {

	var row transactionDetailRow
	var query *gorm.DB

	switch strings.ToUpper(strings.TrimSpace(transactionType)) {

	case "CHAT", "CALL":

		table, prefix, label := "user_chat_histories", "chat", "Chat with "
		if strings.EqualFold(strings.TrimSpace(transactionType), "CALL") {
			table, prefix, label = "user_call_histories", "call", "Call with "
		}

		query = r.db.
			Table(table+" h").
			Joins("LEFT JOIN astrologers a ON a.id = h.astrologerId").
			Select(`
				h.id,
				CONCAT('`+label+`', COALESCE(a.name, 'Astrologer')) AS title,
				COALESCE(h.`+prefix+`HistoryNumber, '') AS transaction_number,
				UPPER('`+prefix+`') AS transaction_type,
				COALESCE(h.deductionAmount, 0) AS amount,
				0 AS is_credit,
				COALESCE(h.`+prefix+`Status, '') AS status,
				'`+prefix+`' AS icon,
				h.created_at AS created_at,
				COALESCE(h.`+prefix+`Type, '') AS session_type,
				COALESCE(h.`+prefix+`Duration, 0) AS duration_seconds,
				`+astrologerDetailColumns).
			Where("h.id = ? AND h.userId = ?", id, userID)

	case "ORDER":

		query = r.db.
			Table("user_orders uo").
			Joins("LEFT JOIN astromall_products ap ON ap.id = uo.astromallProductId").
			Joins("LEFT JOIN order_addresses oa ON oa.id = uo.orderAddressId").
			Select(`
				uo.id,
				CONCAT('Buy ', COALESCE(ap.name, 'Product')) AS title,
				CONCAT('ORD', uo.id) AS transaction_number,
				'ORDER' AS transaction_type,
				COALESCE(ap.amount, 0) AS amount,
				0 AS is_credit,
				'SUCCESS' AS status,
				'shopping' AS icon,
				uo.created_at AS created_at,
				COALESCE(ap.id, 0) AS product_id,
				COALESCE(ap.name, '') AS product_name,
				COALESCE(ap.productImage, '') AS product_image,
				COALESCE(ap.price, 0) AS product_price,
				COALESCE(ap.sellingPrice, 0) AS product_selling_price,
				oa.id IS NOT NULL AS has_address,
				COALESCE(oa.name, '') AS address_name,
				COALESCE(oa.phoneNumber, '') AS address_phone,
				COALESCE(oa.flatNo, '') AS address_flat_no,
				COALESCE(oa.locality, '') AS address_locality,
				COALESCE(oa.landmark, '') AS address_landmark,
				COALESCE(oa.city, '') AS address_city,
				COALESCE(oa.state, '') AS address_state,
				COALESCE(oa.country, '') AS address_country,
				COALESCE(oa.pincode, '') AS address_pincode`).
			Where("uo.id = ? AND uo.userId = ?", id, userID)

	default:

		// RECHARGE and every other wallettransaction type.
		query = r.db.
			Table("wallettransaction wt").
			Joins("LEFT JOIN astrologers a ON a.id = wt.astrologerId").
			Joins("LEFT JOIN payment p ON p.userId = wt.userId AND p.orderId = CAST(wt.orderId AS CHAR) COLLATE utf8mb4_unicode_ci").
			Select(`
				wt.id,
				CASE WHEN UPPER(wt.transactionType) = 'RECHARGE' THEN 'Wallet Recharge'
					ELSE REPLACE(COALESCE(wt.transactionType, 'Wallet Transaction'), '_', ' ') END AS title,
				CONCAT('WT', wt.id) AS transaction_number,
				CASE WHEN UPPER(wt.transactionType) = 'RECHARGE' THEN 'RECHARGE'
					ELSE COALESCE(wt.transactionType, '') END AS transaction_type,
				COALESCE(wt.amount, 0) AS amount,
				COALESCE(wt.isCredit, 0) AS is_credit,
				COALESCE(p.paymentStatus, 'SUCCESS') AS status,
				CASE WHEN UPPER(wt.transactionType) = 'RECHARGE' THEN 'wallet'
					WHEN wt.isCredit = 1 THEN 'credit' ELSE 'debit' END AS icon,
				wt.created_at AS created_at,
				p.id IS NOT NULL AS has_payment,
				COALESCE(p.paymentMode, '') AS payment_mode,
				COALESCE(p.paymentReference, '') AS payment_reference,
				COALESCE(p.orderId, CAST(wt.orderId AS CHAR) COLLATE utf8mb4_unicode_ci, '') AS order_id,
				COALESCE(p.paymentStatus, '') AS payment_status,
				COALESCE(p.cashback_amount, 0) AS cashback_amount,
				`+astrologerDetailColumns).
			Where("wt.id = ? AND wt.userId = ?", id, userID)
	}

	result := query.Limit(1).Scan(&row)
	if result.Error != nil {
		return nil, result.Error
	}
	if result.RowsAffected == 0 {
		return nil, gorm.ErrRecordNotFound
	}

	detail := &dto.WalletTransactionDetail{
		ID:                row.ID,
		Title:             row.Title,
		TransactionNumber: row.TransactionNumber,
		TransactionType:   row.TransactionType,
		Amount:            row.Amount,
		IsCredit:          row.IsCredit,
		Status:            row.Status,
		Icon:              row.Icon,
		CreatedAt:         row.CreatedAt,
	}

	if row.AstrologerID > 0 {
		detail.Astrologer = &dto.TransactionAstrologer{
			ID:           row.AstrologerID,
			Name:         row.AstrologerName,
			ProfileImage: row.AstrologerProfileImage,
		}
	}

	if row.TransactionType == "CHAT" || row.TransactionType == "CALL" {
		detail.Session = &dto.TransactionSession{
			SessionType:     row.SessionType,
			Status:          row.Status,
			DurationSeconds: row.DurationSeconds,
		}
	}

	if row.HasPayment {
		detail.Payment = &dto.TransactionPayment{
			PaymentMode:      row.PaymentMode,
			PaymentReference: row.PaymentReference,
			OrderID:          row.OrderID,
			PaymentStatus:    row.PaymentStatus,
			CashbackAmount:   row.CashbackAmount,
		}
	}

	if row.ProductID > 0 {
		detail.Product = &dto.TransactionProduct{
			ID:           row.ProductID,
			Name:         row.ProductName,
			ProductImage: row.ProductImage,
			Price:        row.ProductPrice,
			SellingPrice: row.ProductSellingPrice,
		}
	}

	if row.HasAddress {
		detail.Address = &dto.TransactionAddress{
			Name:        row.AddressName,
			PhoneNumber: row.AddressPhone,
			FlatNo:      row.AddressFlatNo,
			Locality:    row.AddressLocality,
			Landmark:    row.AddressLandmark,
			City:        row.AddressCity,
			State:       row.AddressState,
			Country:     row.AddressCountry,
			Pincode:     row.AddressPincode,
		}
	}

	return detail, nil
}

func (r *walletRepository) GetRechargeOffers() ([]models.RechargeAmount, error) {

	var offers []models.RechargeAmount

	err := r.db.
		Table("rechargeamount").
		Order("amount ASC").
		Find(&offers).Error

	return offers, err
}

func (r *walletRepository) CreatePayment(payment *models.Payment) error {

	return config.DB.Create(payment).Error

}

func (r *walletRepository) UpdatePayment(payment *models.Payment) error {

	return config.DB.Save(payment).Error

}

func (r *walletRepository) FindPaymentByTransactionID(txn string) (*models.Payment, error) {

	var payment models.Payment

	err := config.DB.
		Where("paymentReference=?", txn).
		First(&payment).Error

	if err != nil {

		return nil, err

	}

	return &payment, nil

}

func (r *walletRepository) FindPaymentByOrderID(order string) (*models.Payment, error) {

	var payment models.Payment

	err := config.DB.
		Where("orderId=?", order).
		First(&payment).Error

	if err != nil {

		return nil, err

	}

	return &payment, nil

}

func (r *walletRepository) GetUserByID(userID uint) (*models.User, error) {

	var user models.User

	err := config.DB.
		First(&user, userID).Error

	if err != nil {

		return nil, err

	}

	return &user, nil

}

func (r *walletRepository) GetPaymentByTransactionID(transactionID string) (*models.Payment, error) {

	var payment models.Payment

	err := r.db.
		Where("paymentReference = ?", transactionID).
		First(&payment).Error

	if err != nil {
		return nil, err
	}

	return &payment, nil
}

func (r *walletRepository) GetUserWallet(userID uint) (*models.UserWallet, error) {

	var wallet models.UserWallet

	err := r.db.
		Where("userId = ?", userID).
		First(&wallet).Error

	if err != nil {
		return nil, err
	}

	return &wallet, nil
}

func (r *walletRepository) UpdateUserWallet(wallet *models.UserWallet) error {

	return r.db.Save(wallet).Error
}

func (r *walletRepository) CreateWalletTransaction(
	transaction *models.WalletTransaction,
) error {

	return r.db.Create(transaction).Error
}
