package repository

import (
	"context"
	"sync"

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

type MemoryStore struct {
	mu       sync.Mutex
	brands   map[string]*domain.Brand
	products map[string]*domain.Product
	active   map[int64]string
	posts    map[string]*domain.Post
	hw       map[string]*domain.Homework // key owner|theme
	crawler  map[int64]*domain.CrawlerSession
}

func NewMemory() *MemoryStore {
	return &MemoryStore{
		brands: map[string]*domain.Brand{}, products: map[string]*domain.Product{},
		active: map[int64]string{}, posts: map[string]*domain.Post{},
		hw: map[string]*domain.Homework{}, crawler: map[int64]*domain.CrawlerSession{},
	}
}

func key(uid int64, theme string) string {
	return formatUID(uid) + "|" + theme
}
func formatUID(uid int64) string {
	if uid == 0 {
		return "0"
	}
	neg := uid < 0
	if neg {
		uid = -uid
	}
	var b [32]byte
	i := len(b)
	for uid > 0 {
		i--
		b[i] = byte('0' + uid%10)
		uid /= 10
	}
	if neg {
		i--
		b[i] = '-'
	}
	return string(b[i:])
}

func (s *MemoryStore) SaveBrand(_ context.Context, b *domain.Brand) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	cp := *b
	s.brands[b.ID] = &cp
	return nil
}
func (s *MemoryStore) GetBrand(_ context.Context, id string) (*domain.Brand, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	b, ok := s.brands[id]
	if !ok {
		return nil, domain.ErrNotFound
	}
	cp := *b
	return &cp, nil
}
func (s *MemoryStore) ListBrands(_ context.Context, ownerUID int64) ([]*domain.Brand, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	var out []*domain.Brand
	for _, b := range s.brands {
		if b.OwnerUID == ownerUID {
			cp := *b
			out = append(out, &cp)
		}
	}
	return out, nil
}
func (s *MemoryStore) DeleteBrand(_ context.Context, id string) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	if _, ok := s.brands[id]; !ok {
		return domain.ErrNotFound
	}
	delete(s.brands, id)
	return nil
}

func (s *MemoryStore) SaveProduct(_ context.Context, p *domain.Product) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	cp := *p
	if p.MatchTags != nil {
		cp.MatchTags = append([]string(nil), p.MatchTags...)
	}
	if p.PainPoints != nil {
		cp.PainPoints = append([]string(nil), p.PainPoints...)
	}
	s.products[p.ID] = &cp
	return nil
}
func (s *MemoryStore) GetProduct(_ context.Context, id string) (*domain.Product, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	p, ok := s.products[id]
	if !ok {
		return nil, domain.ErrNotFound
	}
	return copyProduct(p), nil
}
func (s *MemoryStore) ListProducts(_ context.Context, ownerUID int64, brandID string) ([]*domain.Product, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	var out []*domain.Product
	for _, p := range s.products {
		if p.OwnerUID != ownerUID {
			continue
		}
		if brandID != "" && p.BrandID != brandID {
			continue
		}
		out = append(out, copyProduct(p))
	}
	return out, nil
}
func (s *MemoryStore) DeleteProduct(_ context.Context, id string) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	if _, ok := s.products[id]; !ok {
		return domain.ErrNotFound
	}
	delete(s.products, id)
	return nil
}
func copyProduct(p *domain.Product) *domain.Product {
	cp := *p
	if p.MatchTags != nil {
		cp.MatchTags = append([]string(nil), p.MatchTags...)
	}
	if p.PainPoints != nil {
		cp.PainPoints = append([]string(nil), p.PainPoints...)
	}
	return &cp
}

func (s *MemoryStore) GetActiveBrandID(_ context.Context, ownerUID int64) (string, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.active[ownerUID], nil
}
func (s *MemoryStore) SetActiveBrandID(_ context.Context, ownerUID int64, brandID string) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.active[ownerUID] = brandID
	return nil
}

func (s *MemoryStore) SavePost(_ context.Context, p *domain.Post) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	cp := *p
	s.posts[p.ID] = &cp
	return nil
}
func (s *MemoryStore) GetPost(_ context.Context, id string) (*domain.Post, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	p, ok := s.posts[id]
	if !ok {
		return nil, domain.ErrNotFound
	}
	cp := *p
	return &cp, nil
}
func (s *MemoryStore) ListPosts(_ context.Context, ownerUID int64, brandID string) ([]*domain.Post, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	var out []*domain.Post
	for _, p := range s.posts {
		if p.OwnerUID != ownerUID {
			continue
		}
		if brandID != "" && p.BrandID != brandID {
			continue
		}
		cp := *p
		out = append(out, &cp)
	}
	return out, nil
}
func (s *MemoryStore) DeletePost(_ context.Context, id string) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	if _, ok := s.posts[id]; !ok {
		return domain.ErrNotFound
	}
	delete(s.posts, id)
	return nil
}
func (s *MemoryStore) DeletePostsByTheme(_ context.Context, ownerUID int64, themeKey string) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	for id, p := range s.posts {
		if p.OwnerUID == ownerUID && p.ThemeKey == themeKey {
			delete(s.posts, id)
		}
	}
	return nil
}

func (s *MemoryStore) SaveHomework(_ context.Context, h *domain.Homework) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	cp := *h
	s.hw[key(h.OwnerUID, h.ThemeKey)] = &cp
	return nil
}
func (s *MemoryStore) GetHomework(_ context.Context, ownerUID int64, themeKey string) (*domain.Homework, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	h, ok := s.hw[key(ownerUID, themeKey)]
	if !ok {
		return nil, domain.ErrNotFound
	}
	cp := *h
	return &cp, nil
}
func (s *MemoryStore) ListHomework(_ context.Context, ownerUID int64) ([]*domain.Homework, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	var out []*domain.Homework
	for _, h := range s.hw {
		if h.OwnerUID == ownerUID {
			cp := *h
			out = append(out, &cp)
		}
	}
	return out, nil
}
func (s *MemoryStore) DeleteHomework(_ context.Context, ownerUID int64, themeKey string) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	k := key(ownerUID, themeKey)
	if _, ok := s.hw[k]; !ok {
		return domain.ErrNotFound
	}
	delete(s.hw, k)
	return nil
}

func (s *MemoryStore) GetCrawlerSession(_ context.Context, ownerUID int64) (*domain.CrawlerSession, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	c, ok := s.crawler[ownerUID]
	if !ok || c.StorageStateEnc == "" {
		return nil, domain.ErrNotFound
	}
	cp := *c
	return &cp, nil
}
func (s *MemoryStore) SetCrawlerSession(_ context.Context, sess *domain.CrawlerSession) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	cp := *sess
	s.crawler[sess.OwnerUID] = &cp
	return nil
}
func (s *MemoryStore) ClearCrawlerSession(_ context.Context, ownerUID int64) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	delete(s.crawler, ownerUID)
	return nil
}

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