package galon

import (
	"context"
	"errors"

	"github.com/jackc/pgx/v5"
	"github.com/jackc/pgx/v5/pgxpool"

	"github.com/TaruLab/keluargamart-backend/internal/db"
)

// Repository implements Store on a pgx pool.
type Repository struct {
	pool *pgxpool.Pool
	q    *db.Queries
}

func NewRepository(pool *pgxpool.Pool) *Repository {
	return &Repository{pool: pool, q: db.New(pool)}
}

func (r *Repository) Begin(ctx context.Context) (Tx, error) {
	return r.pool.Begin(ctx)
}

// queries binds the sqlc queries to the caller's transaction, falling back to
// the pool only when the caller is not inside one.
func (r *Repository) queries(q db.DBTX) *db.Queries {
	if q == nil {
		return r.q
	}
	return db.New(q)
}

func (r *Repository) CreateTransaction(ctx context.Context, q db.DBTX, p db.CreateGalonTransactionParams) (db.GalonTransaction, error) {
	return r.queries(q).CreateGalonTransaction(ctx, p)
}

func (r *Repository) SetGalonLoan(ctx context.Context, q db.DBTX, p db.SetGalonLoanParams) (db.GalonTransaction, error) {
	return r.queries(q).SetGalonLoan(ctx, p)
}

func (r *Repository) GetByID(ctx context.Context, q db.DBTX, id int64) (db.GalonTransaction, error) {
	row, err := r.queries(q).GetGalonTransactionByID(ctx, id)
	if errors.Is(err, pgx.ErrNoRows) {
		return db.GalonTransaction{}, ErrNotFound
	}
	return row, err
}

func (r *Repository) GetByIDForUpdate(ctx context.Context, q db.DBTX, id int64) (db.GalonTransaction, error) {
	row, err := r.queries(q).GetGalonTransactionByIDForUpdate(ctx, id)
	if errors.Is(err, pgx.ErrNoRows) {
		return db.GalonTransaction{}, ErrNotFound
	}
	return row, err
}

func (r *Repository) SetStatus(ctx context.Context, q db.DBTX, id int64, status string) error {
	n, err := r.queries(q).SetGalonTransactionStatus(ctx, db.SetGalonTransactionStatusParams{ID: id, Status: status})
	if err != nil {
		return err
	}
	if n == 0 {
		return ErrNotFound
	}
	return nil
}

func (r *Repository) List(ctx context.Context, q db.DBTX, f db.ListGalonTransactionsParams) ([]db.GalonTransaction, error) {
	return r.queries(q).ListGalonTransactions(ctx, f)
}

func (r *Repository) Count(ctx context.Context, q db.DBTX, f db.CountGalonTransactionsParams) (int64, error) {
	return r.queries(q).CountGalonTransactions(ctx, f)
}

func (r *Repository) CustomerExists(ctx context.Context, q db.DBTX, id int64) (bool, error) {
	n, err := r.queries(q).CountCustomersActive(ctx, id)
	return n > 0, err
}

func (r *Repository) ProductExists(ctx context.Context, q db.DBTX, id int64) (bool, error) {
	n, err := r.queries(q).CountProductsActive(ctx, id)
	return n > 0, err
}

func (r *Repository) InventoryStatus(ctx context.Context, q db.DBTX, allowedStoreIDs []int64) (db.GalonInventoryStatusRow, error) {
	return r.queries(q).GalonInventoryStatus(ctx, allowedStoreIDs)
}

func (r *Repository) GallonInfoByCode(ctx context.Context, q db.DBTX, code string, storeIDs []int64) (*GallonInfo, error) {
	row, err := r.queries(q).GetGallonInfoByCode(ctx, db.GetGallonInfoByCodeParams{Code: code, AllowedStoreIds: storeIDs})
	if errors.Is(err, pgx.ErrNoRows) {
		return nil, ErrNotFound
	}
	if err != nil {
		return nil, err
	}
	info := &GallonInfo{Code: code, RefillCount: row.RefillCount}
	if row.LastRefillAt.Valid {
		t := row.LastRefillAt.Time
		info.LastRefillAt = &t
	}
	return info, nil
}

func (r *Repository) TransactionsByCode(ctx context.Context, q db.DBTX, code string, storeIDs []int64) ([]db.GalonTransaction, error) {
	return r.queries(q).ListGalonTransactionsByCode(ctx, db.ListGalonTransactionsByCodeParams{Code: code, AllowedStoreIds: storeIDs, Limit: 100})
}
