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 {
	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

	GetGameCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err 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 NewVirtualGameRepository(store *Store) VirtualGameRepository {
	return &VirtualGameRepo{store: store}
}

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) 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) 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)
// }
