package repository import ( "context" "database/sql" "errors" "fmt" dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/jackc/pgx/v5/pgtype" ) type VirtualGameRepository interface { CountVirtualGameProviders(ctx context.Context) (int64, error) CreateVirtualGameProvider(ctx context.Context, arg dbgen.CreateVirtualGameProviderParams) (dbgen.VirtualGameProvider, error) DeleteVirtualGameProvider(ctx context.Context, providerID string) error GetVirtualGameProviderByID(ctx context.Context, providerID string) (dbgen.VirtualGameProvider, error) ListVirtualGameProviders(ctx context.Context, limit, offset int32) ([]dbgen.VirtualGameProvider, error) UpdateVirtualGameProviderEnabled(ctx context.Context, providerID string, enabled bool) (dbgen.VirtualGameProvider, error) CreateVirtualGameSession(ctx context.Context, session *domain.VirtualGameSession) error GetVirtualGameSessionByToken(ctx context.Context, token string) (*domain.VirtualGameSession, error) UpdateVirtualGameSessionStatus(ctx context.Context, id int64, status string) error CreateVirtualGameTransaction(ctx context.Context, tx *domain.VirtualGameTransaction) error GetVirtualGameTransactionByExternalID(ctx context.Context, externalID string) (*domain.VirtualGameTransaction, error) UpdateVirtualGameTransactionStatus(ctx context.Context, id int64, status string) error // WithTransaction(ctx context.Context, fn func(ctx context.Context) error) error AddFavoriteGame(ctx context.Context, userID, gameID int64) error RemoveFavoriteGame(ctx context.Context, userID, gameID int64) error ListFavoriteGames(ctx context.Context, userID int64) ([]int64, error) GetGameCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error) GetUserGameHistory(ctx context.Context, userID int64) ([]domain.VirtualGameHistory, error) CreateVirtualGameHistory(ctx context.Context, his *domain.VirtualGameHistory) error } type VirtualGameRepo struct { store *Store } // GetGameCounts implements VirtualGameRepository. // func (r *VirtualGameRepo) GetGameCounts(ctx context.Context, filter domain.ReportFilter) (total int64, active int64, inactive int64, err error) { // panic("unimplemented") // } // func convertDBVirtualGameProvider(p dbgen.VirtualGameProvider) domain.VirtualGameProvider { // var logoDark *string // if p.LogoDark.Valid { // logoDark = &p.LogoDark.String // } // var logoLight *string // if p.LogoLight.Valid { // logoLight = &p.LogoLight.String // } // return domain.VirtualGameProvider{ // // ID: p.ID, // ProviderID: p.ProviderID, // ProviderName: p.ProviderName, // LogoDark: logoDark, // LogoLight: logoLight, // Enabled: p.Enabled, // CreatedAt: p.CreatedAt.Time, // UpdatedAt: &p.UpdatedAt.Time, // } // } func ConvertCreateVirtualGameProvider(p domain.VirtualGameProvider) dbgen.CreateVirtualGameProviderParams { return dbgen.CreateVirtualGameProviderParams{ ProviderID: p.ProviderID, ProviderName: p.ProviderName, LogoDark: pgtype.Text{String: func() string { if p.LogoDark != nil { return *p.LogoDark } return "" }(), Valid: p.LogoDark != nil}, LogoLight: pgtype.Text{String: func() string { if p.LogoLight != nil { return *p.LogoLight } return "" }(), Valid: p.LogoLight != nil}, Enabled: p.Enabled, // CreatedAt: time.Now(), } } func NewVirtualGameRepository(store *Store) VirtualGameRepository { return &VirtualGameRepo{store: store} } func (r *VirtualGameRepo) CreateVirtualGameProvider(ctx context.Context, arg dbgen.CreateVirtualGameProviderParams) (dbgen.VirtualGameProvider, error) { return r.store.queries.CreateVirtualGameProvider(ctx, arg) } func (r *VirtualGameRepo) DeleteVirtualGameProvider(ctx context.Context, providerID string) error { return r.store.queries.DeleteVirtualGameProvider(ctx, providerID) } func (r *VirtualGameRepo) GetVirtualGameProviderByID(ctx context.Context, providerID string) (dbgen.VirtualGameProvider, error) { return r.store.queries.GetVirtualGameProviderByID(ctx, providerID) } func (r *VirtualGameRepo) ListVirtualGameProviders(ctx context.Context, limit, offset int32) ([]dbgen.VirtualGameProvider, error) { args := dbgen.ListVirtualGameProvidersParams{ Limit: limit, Offset: offset, } return r.store.queries.ListVirtualGameProviders(ctx, args) } func (r *VirtualGameRepo) UpdateVirtualGameProviderEnabled(ctx context.Context, providerID string, enabled bool) (dbgen.VirtualGameProvider, error) { params := dbgen.UpdateVirtualGameProviderEnabledParams{ ProviderID: providerID, Enabled: enabled, } return r.store.queries.UpdateVirtualGameProviderEnabled(ctx, params) } func (r *VirtualGameRepo) AddFavoriteGame(ctx context.Context, userID, gameID int64) error { params := dbgen.AddFavoriteGameParams{ UserID: userID, GameID: gameID, } return r.store.queries.AddFavoriteGame(ctx, params) } func (r *VirtualGameRepo) RemoveFavoriteGame(ctx context.Context, userID, gameID int64) error { params := dbgen.RemoveFavoriteGameParams{ UserID: userID, GameID: gameID, } return r.store.queries.RemoveFavoriteGame(ctx, params) } func (r *VirtualGameRepo) ListFavoriteGames(ctx context.Context, userID int64) ([]int64, error) { return r.store.queries.ListFavoriteGames(ctx, userID) } func (r *VirtualGameRepo) CountVirtualGameProviders(ctx context.Context) (int64, error) { return r.store.queries.CountVirtualGameProviders(ctx) } func (r *VirtualGameRepo) CreateVirtualGameSession(ctx context.Context, session *domain.VirtualGameSession) error { params := dbgen.CreateVirtualGameSessionParams{ UserID: session.UserID, GameID: session.GameID, SessionToken: session.SessionToken, Currency: session.Currency, Status: session.Status, ExpiresAt: pgtype.Timestamptz{Time: session.ExpiresAt, Valid: true}, } _, err := r.store.queries.CreateVirtualGameSession(ctx, params) return err } func (r *VirtualGameRepo) GetVirtualGameSessionByToken(ctx context.Context, token string) (*domain.VirtualGameSession, error) { dbSession, err := r.store.queries.GetVirtualGameSessionByToken(ctx, token) if err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, nil } return nil, err } return &domain.VirtualGameSession{ ID: dbSession.ID, UserID: dbSession.UserID, GameID: dbSession.GameID, SessionToken: dbSession.SessionToken, Currency: dbSession.Currency, Status: dbSession.Status, CreatedAt: dbSession.CreatedAt.Time, UpdatedAt: dbSession.UpdatedAt.Time, ExpiresAt: dbSession.ExpiresAt.Time, }, nil } func (r *VirtualGameRepo) UpdateVirtualGameSessionStatus(ctx context.Context, id int64, status string) error { return r.store.queries.UpdateVirtualGameSessionStatus(ctx, dbgen.UpdateVirtualGameSessionStatusParams{ ID: id, Status: status, }) } func (r *VirtualGameRepo) CreateVirtualGameTransaction(ctx context.Context, tx *domain.VirtualGameTransaction) error { params := dbgen.CreateVirtualGameTransactionParams{ SessionID: tx.SessionID, UserID: tx.UserID, WalletID: tx.WalletID, TransactionType: tx.TransactionType, Amount: tx.Amount, Currency: tx.Currency, ExternalTransactionID: tx.ExternalTransactionID, Status: tx.Status, } _, err := r.store.queries.CreateVirtualGameTransaction(ctx, params) return err } func (r *VirtualGameRepo) CreateVirtualGameHistory(ctx context.Context, his *domain.VirtualGameHistory) error { params := dbgen.CreateVirtualGameHistoryParams{ SessionID: pgtype.Text{String: his.SessionID, Valid: true}, UserID: his.UserID, // WalletID: pgtype.Int8{Int64: *his.WalletID, Valid: true}, TransactionType: his.TransactionType, Amount: his.Amount, Currency: his.Currency, ExternalTransactionID: his.ExternalTransactionID, Status: his.Status, } _, err := r.store.queries.CreateVirtualGameHistory(ctx, params) return err } func (r *VirtualGameRepo) GetVirtualGameTransactionByExternalID(ctx context.Context, externalID string) (*domain.VirtualGameTransaction, error) { dbTx, err := r.store.queries.GetVirtualGameTransactionByExternalID(ctx, externalID) if err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, nil } return nil, err } return &domain.VirtualGameTransaction{ ID: dbTx.ID, SessionID: dbTx.SessionID, UserID: dbTx.UserID, WalletID: dbTx.WalletID, TransactionType: dbTx.TransactionType, Amount: dbTx.Amount, Currency: dbTx.Currency, ExternalTransactionID: dbTx.ExternalTransactionID, Status: dbTx.Status, CreatedAt: dbTx.CreatedAt.Time, UpdatedAt: dbTx.UpdatedAt.Time, }, nil } func (r *VirtualGameRepo) UpdateVirtualGameTransactionStatus(ctx context.Context, id int64, status string) error { return r.store.queries.UpdateVirtualGameTransactionStatus(ctx, dbgen.UpdateVirtualGameTransactionStatusParams{ ID: id, Status: status, }) } func (r *VirtualGameRepo) GetGameCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error) { query := `SELECT COUNT(*) as total, COUNT(CASE WHEN is_active = true THEN 1 END) as active, COUNT(CASE WHEN is_active = false THEN 1 END) as inactive FROM virtual_games` args := []interface{}{} argPos := 1 // Add filters if provided if filter.StartTime.Valid { query += fmt.Sprintf(" WHERE created_at >= $%d", argPos) args = append(args, filter.StartTime.Value) argPos++ } if filter.EndTime.Valid { query += fmt.Sprintf(" AND created_at <= $%d", argPos) args = append(args, filter.EndTime.Value) argPos++ } row := r.store.conn.QueryRow(ctx, query, args...) err = row.Scan(&total, &active, &inactive) if err != nil { return 0, 0, 0, fmt.Errorf("failed to get game counts: %w", err) } return total, active, inactive, nil } func (r *VirtualGameRepo) GetUserGameHistory(ctx context.Context, userID int64) ([]domain.VirtualGameHistory, error) { query := `SELECT game_id FROM virtual_game_histories WHERE user_id = $1 AND transaction_type = 'BET' ORDER BY created_at DESC LIMIT 100` rows, err := r.store.conn.Query(ctx, query, userID) if err != nil { return nil, err } defer rows.Close() var history []domain.VirtualGameHistory for rows.Next() { var tx domain.VirtualGameHistory if err := rows.Scan(&tx.GameID); err == nil { history = append(history, tx) } } return history, nil } // func (r *VirtualGameRepo) WithTransaction(ctx context.Context, fn func(ctx context.Context) error) error { // _, tx, err := r.store.BeginTx(ctx) // if err != nil { // return err // } // txCtx := context.WithValue(ctx, contextTxKey, tx) // defer func() { // if p := recover(); p != nil { // tx.Rollback(ctx) // panic(p) // } // }() // err = fn(txCtx) // if err != nil { // tx.Rollback(ctx) // return err // } // return tx.Commit(ctx) // }