package repository

import (
	"context"
	"sync"

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

type MemoryStore struct {
	mu       sync.Mutex
	trends   map[int64][]*domain.TrendItem
	elements map[string]*domain.Element
	sessions map[string]*domain.Session // by session id
	active   map[int64]string           // owner -> session id
}

func NewMemory() *MemoryStore {
	return &MemoryStore{
		trends:   map[int64][]*domain.TrendItem{},
		elements: map[string]*domain.Element{},
		sessions: map[string]*domain.Session{},
		active:   map[int64]string{},
	}
}

func (s *MemoryStore) ListTrends(_ context.Context, ownerUID int64) ([]*domain.TrendItem, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	list := s.trends[ownerUID]
	out := make([]*domain.TrendItem, 0, len(list))
	for _, t := range list {
		cp := *t
		if t.Keywords != nil {
			cp.Keywords = append([]string(nil), t.Keywords...)
		}
		if t.Samples != nil {
			cp.Samples = append([]string(nil), t.Samples...)
		}
		out = append(out, &cp)
	}
	return out, nil
}

func (s *MemoryStore) SaveTrends(_ context.Context, ownerUID int64, items []*domain.TrendItem) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	cp := make([]*domain.TrendItem, 0, len(items))
	for _, t := range items {
		x := *t
		if t.Keywords != nil {
			x.Keywords = append([]string(nil), t.Keywords...)
		}
		if t.Samples != nil {
			x.Samples = append([]string(nil), t.Samples...)
		}
		cp = append(cp, &x)
	}
	s.trends[ownerUID] = cp
	return nil
}

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

func (s *MemoryStore) SaveElement(_ context.Context, e *domain.Element) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	cp := *e
	s.elements[e.ID] = &cp
	return nil
}

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

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

func (s *MemoryStore) ListSessions(_ context.Context, ownerUID int64) ([]*domain.Session, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	var out []*domain.Session
	for _, sess := range s.sessions {
		if sess.OwnerUID == ownerUID {
			out = append(out, copySession(sess))
		}
	}
	// sort by updated_at desc
	for i := 0; i < len(out); i++ {
		for j := i + 1; j < len(out); j++ {
			if out[j].UpdatedAt > out[i].UpdatedAt {
				out[i], out[j] = out[j], out[i]
			}
		}
	}
	if len(out) > 50 {
		out = out[:50]
	}
	return out, nil
}

func (s *MemoryStore) GetSession(_ context.Context, ownerUID int64) (*domain.Session, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	if id, ok := s.active[ownerUID]; ok {
		if sess, ok2 := s.sessions[id]; ok2 && sess.OwnerUID == ownerUID {
			return copySession(sess), nil
		}
	}
	var best *domain.Session
	for _, sess := range s.sessions {
		if sess.OwnerUID != ownerUID {
			continue
		}
		if best == nil || sess.UpdatedAt > best.UpdatedAt {
			best = sess
		}
	}
	if best == nil {
		return nil, domain.ErrNotFound
	}
	s.active[ownerUID] = best.ID
	return copySession(best), nil
}

func (s *MemoryStore) GetSessionByID(_ context.Context, ownerUID int64, id string) (*domain.Session, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	sess, ok := s.sessions[id]
	if !ok || sess.OwnerUID != ownerUID {
		return nil, domain.ErrNotFound
	}
	return copySession(sess), nil
}

func (s *MemoryStore) SaveSession(_ context.Context, sess *domain.Session) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.sessions[sess.ID] = copySession(sess)
	return nil
}

func (s *MemoryStore) DeleteSessionByID(_ context.Context, ownerUID int64, id string) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	sess, ok := s.sessions[id]
	if !ok || sess.OwnerUID != ownerUID {
		return domain.ErrNotFound
	}
	delete(s.sessions, id)
	if s.active[ownerUID] == id {
		delete(s.active, ownerUID)
	}
	return nil
}

func (s *MemoryStore) GetActiveSessionID(_ context.Context, ownerUID int64) (string, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	id, ok := s.active[ownerUID]
	if !ok || id == "" {
		return "", domain.ErrNotFound
	}
	return id, nil
}

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

func copySession(sess *domain.Session) *domain.Session {
	cp := *sess
	if sess.Messages != nil {
		cp.Messages = append([]domain.ChatMessage(nil), sess.Messages...)
		for i := range cp.Messages {
			if sess.Messages[i].Draft != nil {
				d := *sess.Messages[i].Draft
				cp.Messages[i].Draft = &d
			}
		}
	}
	if sess.PinnedElementIDs != nil {
		cp.PinnedElementIDs = append([]string(nil), sess.PinnedElementIDs...)
	}
	return &cp
}

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