package repository

import (
	"context"
	"sync"

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

type MemoryStore struct {
	mu   sync.Mutex
	byID map[string]*domain.Notification
}

func NewMemory() *MemoryStore {
	return &MemoryStore{byID: map[string]*domain.Notification{}}
}

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

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

func (s *MemoryStore) UnreadCount(_ context.Context, ownerUID int64) (int64, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	var c int64
	for _, n := range s.byID {
		if n.OwnerUID == ownerUID && n.ReadAt == 0 {
			c++
		}
	}
	return c, nil
}

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

func (s *MemoryStore) MarkRead(_ context.Context, ownerUID int64, id string) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	n, ok := s.byID[id]
	if !ok {
		return domain.ErrNotFound
	}
	if n.OwnerUID != ownerUID {
		return domain.ErrForbidden
	}
	if n.ReadAt == 0 {
		n.ReadAt = domain.NowNano()
	}
	return nil
}

func (s *MemoryStore) MarkAllRead(_ context.Context, ownerUID int64) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	now := domain.NowNano()
	for _, n := range s.byID {
		if n.OwnerUID == ownerUID && n.ReadAt == 0 {
			n.ReadAt = now
		}
	}
	return nil
}

func (s *MemoryStore) FindLatestByJobRef(_ context.Context, ownerUID int64, jobID string) (*domain.Notification, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	var best *domain.Notification
	for _, n := range s.byID {
		if n.OwnerUID != ownerUID || n.Kind != domain.KindJob || n.RefType != domain.RefJob || n.RefID != jobID {
			continue
		}
		if best == nil || n.CreatedAt > best.CreatedAt {
			cp := *n
			best = &cp
		}
	}
	if best == nil {
		return nil, domain.ErrNotFound
	}
	return best, nil
}

func (s *MemoryStore) UpdateJobNotification(_ context.Context, n *domain.Notification, markUnread bool) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	if n == nil || n.ID == "" {
		return domain.ErrNotFound
	}
	if _, ok := s.byID[n.ID]; !ok {
		return domain.ErrNotFound
	}
	cp := *n
	if !markUnread {
		cp.ReadAt = s.byID[n.ID].ReadAt
	}
	s.byID[n.ID] = &cp
	return nil
}

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