package repositories

import (
	"errors"
	"strconv"

	usermodel "astrology-api/models/usermodel"

	"gorm.io/gorm"
)

//////////////////////////////////////////////////////////////
// Astrology Repository
//////////////////////////////////////////////////////////////

type AstrologyRepository interface {

	//----------------------------------------------------------
	// CONFIG
	//----------------------------------------------------------

	GetVedicAPIKey() (string, error)

	GetServicePrice(flagName string) (float64, error)

	//----------------------------------------------------------
	// WALLET
	//----------------------------------------------------------

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

	UpdateWallet(tx *gorm.DB, wallet *usermodel.UserWallet) error

	CreateWalletTransaction(tx *gorm.DB, transaction *usermodel.WalletTransaction) error

	//----------------------------------------------------------
	// KUNDALI MATCHING
	//----------------------------------------------------------

	CreateMatching(tx *gorm.DB, matching *usermodel.KundaliMatching) error

	UpdateMatching(tx *gorm.DB, matching *usermodel.KundaliMatching) error

	FindUserMatching(userID uint, matchingID uint) (*usermodel.KundaliMatching, error)

	GetUserMatchings(userID uint, search string, page int, limit int) ([]usermodel.KundaliMatching, error)

	CountUserMatchings(userID uint, search string) (int64, error)

	DeleteMatching(userID uint, matchingID uint) error

	//----------------------------------------------------------
	// HOROSCOPE ACTIVITY
	//----------------------------------------------------------

	GetAstrologerIDByUserID(userID uint) (uint, error)

	CreateHoroscopeRequest(row *usermodel.HoroscopeRequest) error

	CreateHoroscopeFeedback(row *usermodel.HoroscopeFeedback) error

	GetHoroscopeSigns() ([]usermodel.HororscopeSign, error)
}

type astrologyRepository struct {
	db *gorm.DB
}

func NewAstrologyRepository(db *gorm.DB) AstrologyRepository {
	return &astrologyRepository{db: db}
}

//////////////////////////////////////////////////////////////
// Config
//////////////////////////////////////////////////////////////

func (r *astrologyRepository) GetVedicAPIKey() (string, error) {

	var flag usermodel.SystemFlag

	err := r.db.
		Where("name = ?", "vedicAstroAPI").
		First(&flag).Error

	if err != nil {
		return "", errors.New("vedic api key is not configured")
	}

	return flag.Value, nil
}

// GetServicePrice reads a price from systemflag. A missing or unparsable row is
// treated as free rather than an error, so adding a service does not require a
// migration before it can be used.
func (r *astrologyRepository) GetServicePrice(flagName string) (float64, error) {

	var flag usermodel.SystemFlag

	err := r.db.
		Where("name = ? AND isActive = ?", flagName, "1").
		First(&flag).Error

	if err != nil {

		if errors.Is(err, gorm.ErrRecordNotFound) {
			return 0, nil
		}

		return 0, err
	}

	price, convErr := strconv.ParseFloat(flag.Value, 64)

	if convErr != nil || price < 0 {
		return 0, nil
	}

	return price, nil
}

//////////////////////////////////////////////////////////////
// Wallet
//////////////////////////////////////////////////////////////

func (r *astrologyRepository) GetUserWallet(userID uint) (*usermodel.UserWallet, error) {

	var wallet usermodel.UserWallet

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

	if err != nil {
		return nil, err
	}

	return &wallet, nil
}

func (r *astrologyRepository) UpdateWallet(
	tx *gorm.DB,
	wallet *usermodel.UserWallet,
) error {

	return tx.Save(wallet).Error
}

func (r *astrologyRepository) CreateWalletTransaction(
	tx *gorm.DB,
	transaction *usermodel.WalletTransaction,
) error {

	return tx.Create(transaction).Error
}

//////////////////////////////////////////////////////////////
// Kundali Matching
//////////////////////////////////////////////////////////////

func (r *astrologyRepository) CreateMatching(
	tx *gorm.DB,
	matching *usermodel.KundaliMatching,
) error {

	return tx.Create(matching).Error
}

func (r *astrologyRepository) UpdateMatching(
	tx *gorm.DB,
	matching *usermodel.KundaliMatching,
) error {

	return tx.Save(matching).Error
}

func (r *astrologyRepository) FindUserMatching(
	userID uint,
	matchingID uint,
) (*usermodel.KundaliMatching, error) {

	var matching usermodel.KundaliMatching

	err := r.db.
		Where("id = ? AND createdBy = ? AND isDelete = ?", matchingID, userID, false).
		First(&matching).Error

	if err != nil {
		return nil, err
	}

	return &matching, nil
}

func (r *astrologyRepository) GetUserMatchings(
	userID uint,
	search string,
	page int,
	limit int,
) ([]usermodel.KundaliMatching, error) {

	var list []usermodel.KundaliMatching

	query := r.db.
		Where("createdBy = ? AND isDelete = ?", userID, false)

	if search != "" {
		query = query.Where(
			"boyName LIKE ? OR girlName LIKE ?",
			"%"+search+"%",
			"%"+search+"%",
		)
	}

	err := query.
		Order("id DESC").
		Offset((page - 1) * limit).
		Limit(limit).
		Find(&list).Error

	return list, err
}

func (r *astrologyRepository) CountUserMatchings(
	userID uint,
	search string,
) (int64, error) {

	var total int64

	query := r.db.
		Model(&usermodel.KundaliMatching{}).
		Where("createdBy = ? AND isDelete = ?", userID, false)

	if search != "" {
		query = query.Where(
			"boyName LIKE ? OR girlName LIKE ?",
			"%"+search+"%",
			"%"+search+"%",
		)
	}

	err := query.Count(&total).Error

	return total, err
}

// DeleteMatching is a soft delete, matching how kundalis are removed.
func (r *astrologyRepository) DeleteMatching(
	userID uint,
	matchingID uint,
) error {

	result := r.db.
		Model(&usermodel.KundaliMatching{}).
		Where("id = ? AND createdBy = ?", matchingID, userID).
		Update("isDelete", true)

	if result.Error != nil {
		return result.Error
	}

	if result.RowsAffected == 0 {
		return errors.New("matching not found")
	}

	return nil
}

func (r *astrologyRepository) GetAstrologerIDByUserID(userID uint) (uint, error) {

	var id uint

	err := r.db.
		Table("astrologers").
		Select("id").
		Where("userId = ?", userID).
		Order("id ASC").
		Limit(1).
		Scan(&id).Error

	if err != nil {
		return 0, err
	}

	if id == 0 {
		return 0, errors.New("astrologer not found")
	}

	return id, nil
}

func (r *astrologyRepository) CreateHoroscopeRequest(row *usermodel.HoroscopeRequest) error {
	return r.db.Create(row).Error
}

func (r *astrologyRepository) CreateHoroscopeFeedback(row *usermodel.HoroscopeFeedback) error {
	return r.db.Create(row).Error
}

// GetHoroscopeSigns returns the active, non-deleted signs.
func (r *astrologyRepository) GetHoroscopeSigns() ([]usermodel.HororscopeSign, error) {

	var signs []usermodel.HororscopeSign

	err := r.db.
		Where("isActive = ? AND isDelete = ?", true, false).
		Order("id ASC").
		Find(&signs).Error

	return signs, err
}
