package gateway

import (
	"context"
	"sync"

	"github.com/tech/sendico/gateway/mntx/storage"
	"github.com/tech/sendico/gateway/mntx/storage/model"
)

// mockRepository implements storage.Repository for tests.
type mockRepository struct {
	payouts *cardPayoutStore
}

func newMockRepository() *mockRepository {
	return &mockRepository{
		payouts: newCardPayoutStore(),
	}
}

func (r *mockRepository) Payouts() storage.PayoutsStore {
	return r.payouts
}

// cardPayoutStore implements storage.PayoutsStore for tests.
type cardPayoutStore struct {
	mu   sync.RWMutex
	data map[string]*model.CardPayout
}

func payoutStoreKey(state *model.CardPayout) string {
	if state == nil {
		return ""
	}
	if ref := state.OperationRef; ref != "" {
		return ref
	}
	return state.PaymentRef
}

func newCardPayoutStore() *cardPayoutStore {
	return &cardPayoutStore{
		data: make(map[string]*model.CardPayout),
	}
}

func (s *cardPayoutStore) FindByIdempotencyKey(_ context.Context, key string) (*model.CardPayout, error) {
	s.mu.RLock()
	defer s.mu.RUnlock()
	for _, v := range s.data {
		if v.IdempotencyKey == key {
			return v, nil
		}
	}
	return nil, nil //nolint:nilnil // test store: payout not found by idempotency key
}

func (s *cardPayoutStore) FindByOperationRef(_ context.Context, ref string) (*model.CardPayout, error) {
	s.mu.RLock()
	defer s.mu.RUnlock()
	for _, v := range s.data {
		if v.OperationRef == ref {
			return v, nil
		}
	}
	return nil, nil //nolint:nilnil // test store: payout not found by operation ref
}

func (s *cardPayoutStore) FindByPaymentID(_ context.Context, id string) (*model.CardPayout, error) {
	s.mu.RLock()
	defer s.mu.RUnlock()
	for _, v := range s.data {
		if v.PaymentRef == id {
			return v, nil
		}
	}
	return nil, nil //nolint:nilnil // test store: payout not found by payment id
}

func (s *cardPayoutStore) Upsert(_ context.Context, record *model.CardPayout) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.data[payoutStoreKey(record)] = record
	return nil
}

// Save is a helper for tests to pre-populate data.
func (s *cardPayoutStore) Save(state *model.CardPayout) {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.data[payoutStoreKey(state)] = state
}

// Get is a helper for tests to retrieve data.
func (s *cardPayoutStore) Get(id string) (*model.CardPayout, bool) {
	s.mu.RLock()
	defer s.mu.RUnlock()
	if v, ok := s.data[id]; ok {
		return v, true
	}
	for _, v := range s.data {
		if v.PaymentRef == id || v.OperationRef == id {
			return v, true
		}
	}
	return nil, false
}
