package tool

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]Tool
	byName map[string]string // ad → kimlik
}

var _ Repository = (*MemoryRepo)(nil)

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

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

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

func (r *MemoryRepo) GetByName(_ context.Context, name string) (*Tool, 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, t *Tool) error {
	if t == nil || t.ID == "" {
		return errors.New("tool: kimlik gerekli")
	}
	r.mu.Lock()
	defer r.mu.Unlock()
	old, ok := r.byID[t.ID]
	if !ok {
		return ErrNotFound
	}
	if owner, taken := r.byName[t.Name]; taken && owner != t.ID {
		return ErrDuplicateName
	}
	if old.Name != t.Name {
		delete(r.byName, old.Name)
	}
	r.byID[t.ID] = t.Clone()
	r.byName[t.Name] = t.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) ([]Tool, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	q := strings.ToLower(strings.TrimSpace(f.Query))
	out := make([]Tool, 0, len(r.byID))
	for _, t := range r.byID {
		if matches(t, f, q) {
			out = append(out, t.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 []Tool{}, nil
	}
	out = out[offset:]
	if limit > 0 && limit < len(out) {
		out = out[:limit]
	}
	return out, nil
}

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