package updates

import (
	"context"
	"sync"

	"github.com/go-faster/errors"
)

var _ StateStorage = (*memStorage)(nil)

type memStorage struct {
	states   map[int64]State
	channels map[int64]map[int64]int
	mux      sync.Mutex
}

func newMemStorage() *memStorage {
	return &memStorage{
		states:   map[int64]State{},
		channels: map[int64]map[int64]int{},
	}
}

func (s *memStorage) GetState(ctx context.Context, userID int64) (state State, found bool, err error) {
	s.mux.Lock()
	defer s.mux.Unlock()

	state, found = s.states[userID]
	return
}

func (s *memStorage) SetState(ctx context.Context, userID int64, state State) error {
	s.mux.Lock()
	defer s.mux.Unlock()

	s.states[userID] = state
	s.channels[userID] = map[int64]int{}
	return nil
}

func (s *memStorage) SetPts(ctx context.Context, userID int64, pts int) error {
	s.mux.Lock()
	defer s.mux.Unlock()

	state, ok := s.states[userID]
	if !ok {
		return errors.New("internalState not found")
	}

	state.Pts = pts
	s.states[userID] = state
	return nil
}

func (s *memStorage) SetQts(ctx context.Context, userID int64, qts int) error {
	s.mux.Lock()
	defer s.mux.Unlock()

	state, ok := s.states[userID]
	if !ok {
		return errors.New("internalState not found")
	}

	state.Qts = qts
	s.states[userID] = state
	return nil
}

func (s *memStorage) SetDate(ctx context.Context, userID int64, date int) error {
	s.mux.Lock()
	defer s.mux.Unlock()

	state, ok := s.states[userID]
	if !ok {
		return errors.New("internalState not found")
	}

	state.Date = date
	s.states[userID] = state
	return nil
}

func (s *memStorage) SetSeq(ctx context.Context, userID int64, seq int) error {
	s.mux.Lock()
	defer s.mux.Unlock()

	state, ok := s.states[userID]
	if !ok {
		return errors.New("internalState not found")
	}

	state.Seq = seq
	s.states[userID] = state
	return nil
}

func (s *memStorage) SetDateSeq(ctx context.Context, userID int64, date, seq int) error {
	s.mux.Lock()
	defer s.mux.Unlock()

	state, ok := s.states[userID]
	if !ok {
		return errors.New("internalState not found")
	}

	state.Date = date
	state.Seq = seq
	s.states[userID] = state
	return nil
}

func (s *memStorage) SetChannelPts(ctx context.Context, userID, channelID int64, pts int) error {
	s.mux.Lock()
	defer s.mux.Unlock()

	channels, ok := s.channels[userID]
	if !ok {
		return errors.New("user internalState does not exist")
	}

	channels[channelID] = pts
	return nil
}

func (s *memStorage) GetChannelPts(ctx context.Context, userID, channelID int64) (pts int, found bool, err error) {
	s.mux.Lock()
	defer s.mux.Unlock()

	channels, ok := s.channels[userID]
	if !ok {
		return 0, false, nil
	}

	pts, found = channels[channelID]
	return
}

func (s *memStorage) ForEachChannels(ctx context.Context, userID int64, f func(ctx context.Context, channelID int64, pts int) error) error {
	s.mux.Lock()
	defer s.mux.Unlock()

	cmap, ok := s.channels[userID]
	if !ok {
		return errors.New("channels map does not exist")
	}

	for id, pts := range cmap {
		if err := f(ctx, id, pts); err != nil {
			return err
		}
	}

	return nil
}

var _ AccessHasher = (*memAccessHasher)(nil)

type memAccessHasher struct {
	channelHashes map[int64]map[int64]int64
	userHashes    map[int64]map[int64]int64
	mux           sync.Mutex
}

func newMemAccessHasher() *memAccessHasher {
	return &memAccessHasher{
		channelHashes: map[int64]map[int64]int64{},
		userHashes:    map[int64]map[int64]int64{},
	}
}

func (m *memAccessHasher) GetChannelAccessHash(ctx context.Context, forUserID, channelID int64) (accessHash int64, found bool, err error) {
	m.mux.Lock()
	defer m.mux.Unlock()

	accessHashes, ok := m.channelHashes[forUserID]
	if !ok {
		return 0, false, nil
	}

	accessHash, found = accessHashes[channelID]
	return
}

func (m *memAccessHasher) SetChannelAccessHash(ctx context.Context, forUserID, channelID, accessHash int64) error {
	m.mux.Lock()
	defer m.mux.Unlock()

	accessHashes, ok := m.channelHashes[forUserID]
	if !ok {
		accessHashes = map[int64]int64{}
		m.channelHashes[forUserID] = accessHashes
	}

	accessHashes[channelID] = accessHash
	return nil
}

func (m *memAccessHasher) GetUserAccessHash(ctx context.Context, forUserID, userID int64) (accessHash int64, found bool, err error) {
	m.mux.Lock()
	defer m.mux.Unlock()

	accessHashes, ok := m.userHashes[forUserID]
	if !ok {
		return 0, false, nil
	}

	accessHash, found = accessHashes[userID]
	return
}

func (m *memAccessHasher) SetUserAccessHash(ctx context.Context, forUserID, userID, accessHash int64) error {
	m.mux.Lock()
	defer m.mux.Unlock()

	accessHashes, ok := m.userHashes[forUserID]
	if !ok {
		accessHashes = map[int64]int64{}
		m.channelHashes[forUserID] = accessHashes
	}

	accessHashes[userID] = accessHash
	return nil
}
