package repository

import (
	"context"
	"sync"

	"apps/backend/internal/module/threads/domain"
)

// MemoryStore for unit tests.
type MemoryStore struct {
	mu    sync.Mutex
	byID  map[string]*domain.Account
	state map[string]*domain.OAuthState
}

func NewMemory() *MemoryStore {
	return &MemoryStore{
		byID:  map[string]*domain.Account{},
		state: map[string]*domain.OAuthState{},
	}
}

func (s *MemoryStore) Insert(_ context.Context, a *domain.Account) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	cp := *a
	s.byID[a.ID] = &cp
	return nil
}

func (s *MemoryStore) Update(_ context.Context, a *domain.Account) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	if _, ok := s.byID[a.ID]; !ok {
		return domain.ErrNotFound
	}
	cp := *a
	s.byID[a.ID] = &cp
	return nil
}

func (s *MemoryStore) FindByID(_ context.Context, id string) (*domain.Account, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	a, ok := s.byID[id]
	if !ok {
		return nil, domain.ErrNotFound
	}
	cp := *a
	return &cp, nil
}

func (s *MemoryStore) FindByOwnerAndThreadsUser(_ context.Context, ownerUID int64, threadsUserID string) (*domain.Account, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	if threadsUserID == "" {
		return nil, domain.ErrNotFound
	}
	for _, a := range s.byID {
		if a.OwnerUID == ownerUID && a.ThreadsUserID == threadsUserID {
			cp := *a
			return &cp, nil
		}
	}
	return nil, domain.ErrNotFound
}

func (s *MemoryStore) ListByOwner(_ context.Context, ownerUID int64) ([]*domain.Account, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	var out []*domain.Account
	for _, a := range s.byID {
		if a.OwnerUID == ownerUID {
			cp := *a
			out = append(out, &cp)
		}
	}
	return out, nil
}

func (s *MemoryStore) Delete(_ context.Context, id string) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	if _, ok := s.byID[id]; !ok {
		return domain.ErrNotFound
	}
	delete(s.byID, id)
	return nil
}

func (s *MemoryStore) SaveState(_ context.Context, st *domain.OAuthState) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	cp := *st
	s.state[st.State] = &cp
	return nil
}

func (s *MemoryStore) TakeState(_ context.Context, state string) (*domain.OAuthState, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	st, ok := s.state[state]
	if !ok {
		return nil, domain.ErrInvalidState
	}
	delete(s.state, state)
	if st.ExpiresAt > 0 && domain.NowNano() > st.ExpiresAt {
		return nil, domain.ErrInvalidState
	}
	cp := *st
	return &cp, nil
}

var _ domain.Repository = (*MemoryStore)(nil)
