package skill

import (
	"context"
	"errors"
	"sort"
	"strings"
	"sync"
)

// MemoryRepo bellek içi Repository uygulamasıdır (MongoDB yokken geliştirme ve testler).
// Eşzamanlı kullanıma güvenlidir; giriş/çıkışta derin kopya alır.
type MemoryRepo struct {
	mu     sync.RWMutex
	byID   map[string]Skill
	byName map[string]string // ad → kimlik
}

var _ Repository = (*MemoryRepo)(nil)

func NewMemoryRepo() *MemoryRepo {
	return &MemoryRepo{byID: make(map[string]Skill), byName: make(map[string]string)}
}

func (r *MemoryRepo) Create(_ context.Context, s *Skill) error {
	if s == nil || s.ID == "" {
		return errors.New("skill: kimlik gerekli")
	}
	r.mu.Lock()
	defer r.mu.Unlock()
	if _, ok := r.byID[s.ID]; ok {
		return ErrDuplicateName
	}
	if _, ok := r.byName[s.Name]; ok {
		return ErrDuplicateName
	}
	r.byID[s.ID] = s.Clone()
	r.byName[s.Name] = s.ID
	return nil
}

func (r *MemoryRepo) Get(_ context.Context, id string) (*Skill, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	s, ok := r.byID[id]
	if !ok {
		return nil, ErrNotFound
	}
	c := s.Clone()
	return &c, nil
}

func (r *MemoryRepo) GetByName(_ context.Context, name string) (*Skill, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	id, ok := r.byName[name]
	if !ok {
		return nil, ErrNotFound
	}
	c := r.byID[id].Clone()
	return &c, nil
}

func (r *MemoryRepo) Update(_ context.Context, s *Skill) error {
	r.mu.Lock()
	defer r.mu.Unlock()
	old, ok := r.byID[s.ID]
	if !ok {
		return ErrNotFound
	}
	if owner, taken := r.byName[s.Name]; taken && owner != s.ID {
		return ErrDuplicateName
	}
	if old.Name != s.Name {
		delete(r.byName, old.Name)
	}
	r.byID[s.ID] = s.Clone()
	r.byName[s.Name] = s.ID
	return nil
}

func (r *MemoryRepo) Delete(_ context.Context, id string) error {
	r.mu.Lock()
	defer r.mu.Unlock()
	old, ok := r.byID[id]
	if !ok {
		return ErrNotFound
	}
	delete(r.byID, id)
	delete(r.byName, old.Name)
	return nil
}

func (r *MemoryRepo) List(_ context.Context, f ListFilter, limit, offset int) ([]Skill, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	q := strings.ToLower(strings.TrimSpace(f.Query))
	out := make([]Skill, 0, len(r.byID))
	for _, s := range r.byID {
		if matches(s, f, q) {
			out = append(out, s.Clone())
		}
	}
	sort.Slice(out, func(i, j int) bool { return out[i].Name < out[j].Name })
	if offset < 0 {
		offset = 0
	}
	if offset >= len(out) {
		return []Skill{}, nil
	}
	out = out[offset:]
	if limit > 0 && limit < len(out) {
		out = out[:limit]
	}
	return out, nil
}

// matches filtreyi tek skill'e uygular. q küçük harfe çevrilmiş sorgudur.
func matches(s Skill, f ListFilter, q string) bool {
	if f.Status != "" && s.Status != f.Status {
		return false
	}
	if f.Tag != "" {
		found := false
		for _, t := range s.Tags {
			if t == f.Tag {
				found = true
				break
			}
		}
		if !found {
			return false
		}
	}
	if q != "" && !strings.Contains(strings.ToLower(s.Name), q) && !strings.Contains(strings.ToLower(s.Description), q) {
		return false
	}
	return true
}
