package claim

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

// MemoryRepo bellek içi claim deposudur (süreç kapanınca veri gider; testler ve MongoDB'siz çalışma).
// Eşzamanlı kullanıma güvenlidir.
type MemoryRepo struct {
	mu    sync.RWMutex
	items map[string]Claim
}

var _ Repository = (*MemoryRepo)(nil)

// NewMemoryRepo boş bir bellek içi depo üretir.
func NewMemoryRepo() *MemoryRepo { return &MemoryRepo{items: map[string]Claim{}} }

// matches süzgece uyuyor mu? (Mongo uygulamasındaki claimFilter ile AYNI anlam; sözleşme testi denetler.)
func matches(f Filter, c Claim) bool {
	if f.Kinds != nil && !containsKind(f.Kinds, c.Kind) {
		return false
	}
	if f.Statuses != nil && !containsStatus(f.Statuses, c.Status) {
		return false
	}
	if f.Key != "" && c.Key != f.Key {
		return false
	}
	if f.AuthorID != "" && c.AuthorID != f.AuthorID {
		return false
	}
	if q := strings.ToLower(strings.TrimSpace(f.Query)); q != "" && !strings.Contains(strings.ToLower(c.Text), q) {
		return false
	}
	if len(f.OwnOnly) > 0 && containsKind(f.OwnOnly, c.Kind) && (f.ViewerID == "" || c.SubjectID != f.ViewerID) {
		return false
	}
	return true
}

func containsKind(list []string, k string) bool {
	for _, x := range list {
		if x == k {
			return true
		}
	}
	return false
}

func containsStatus(list []Status, s Status) bool {
	for _, x := range list {
		if x == s {
			return true
		}
	}
	return false
}

// sortStable List/Searchable sırası: Key artan, CreatedAt artan, ID.
func sortStable(cs []Claim) {
	sort.SliceStable(cs, func(i, j int) bool {
		if cs[i].Key != cs[j].Key {
			return cs[i].Key < cs[j].Key
		}
		if !cs[i].CreatedAt.Equal(cs[j].CreatedAt) {
			return cs[i].CreatedAt.Before(cs[j].CreatedAt)
		}
		return cs[i].ID < cs[j].ID
	})
}

func (r *MemoryRepo) Create(_ context.Context, c *Claim) error {
	if c == nil || c.ID == "" || c.AuthorID == "" {
		return errors.New("claim: kayıt kimliği ve yazarı gerekli")
	}
	r.mu.Lock()
	defer r.mu.Unlock()
	if _, exists := r.items[c.ID]; exists {
		return ErrDuplicate
	}
	r.items[c.ID] = c.Clone()
	return nil
}

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

func (r *MemoryRepo) Update(_ context.Context, c *Claim) error {
	if c == nil || c.ID == "" || c.AuthorID == "" {
		return errors.New("claim: kayıt kimliği ve yazarı gerekli")
	}
	r.mu.Lock()
	defer r.mu.Unlock()
	if _, ok := r.items[c.ID]; !ok {
		return ErrNotFound
	}
	r.items[c.ID] = c.Clone()
	return nil
}

func (r *MemoryRepo) SetStatus(_ context.Context, id string, status Status, at time.Time) error {
	r.mu.Lock()
	defer r.mu.Unlock()
	c, ok := r.items[id]
	if !ok {
		return ErrNotFound
	}
	c.Status = status
	c.UpdatedAt = at
	r.items[id] = c
	return nil
}

func (r *MemoryRepo) SetEmbedding(_ context.Context, id string, vec []float32, model string) error {
	r.mu.Lock()
	defer r.mu.Unlock()
	c, ok := r.items[id]
	if !ok {
		return ErrNotFound
	}
	c.Embedding = append([]float32(nil), vec...)
	c.EmbedModel = model
	r.items[id] = c
	return nil
}

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

// collect süzgece uyanları kararlı sırada derin kopyalar; withVec=false ise vektörü atar.
func (r *MemoryRepo) collect(f Filter, withVec bool) []Claim {
	r.mu.RLock()
	out := make([]Claim, 0, len(r.items))
	for _, c := range r.items {
		if !matches(f, c) {
			continue
		}
		cp := c.Clone()
		if !withVec {
			cp.Embedding = nil
			cp.EmbedModel = ""
		}
		out = append(out, cp)
	}
	r.mu.RUnlock()
	sortStable(out)
	return out
}

func (r *MemoryRepo) List(_ context.Context, f Filter, limit, offset int) ([]Claim, error) {
	all := r.collect(f, false)
	if offset < 0 {
		offset = 0
	}
	if offset >= len(all) {
		return []Claim{}, nil
	}
	all = all[offset:]
	if limit > 0 && len(all) > limit {
		all = all[:limit]
	}
	return all, nil
}

func (r *MemoryRepo) Count(_ context.Context, f Filter) (int, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	n := 0
	for _, c := range r.items {
		if matches(f, c) {
			n++
		}
	}
	return n, nil
}

func (r *MemoryRepo) ByKey(_ context.Context, kind, key string) ([]Claim, error) {
	if key == "" {
		return []Claim{}, nil // anahtarsız kayıtlar birbirine "aynı anahtar" değildir
	}
	r.mu.RLock()
	out := make([]Claim, 0)
	for _, c := range r.items {
		if c.Kind == kind && c.Key == key {
			cp := c.Clone()
			cp.Embedding = nil
			cp.EmbedModel = ""
			out = append(out, cp)
		}
	}
	r.mu.RUnlock()
	sort.SliceStable(out, func(i, j int) bool {
		if !out[i].CreatedAt.Equal(out[j].CreatedAt) {
			return out[i].CreatedAt.After(out[j].CreatedAt)
		}
		return out[i].ID < out[j].ID
	})
	return out, nil
}

func (r *MemoryRepo) Searchable(_ context.Context, f Filter) ([]Claim, error) {
	return r.collect(f, true), nil
}

// MemoryAuditRepo bellek içi denetim deposudur (yalnızca ekleme). Eşzamanlı kullanıma güvenlidir.
type MemoryAuditRepo struct {
	mu      sync.RWMutex
	entries []AuditEntry
	ids     map[string]struct{}
}

var _ AuditRepository = (*MemoryAuditRepo)(nil)

// NewMemoryAuditRepo boş bir bellek içi denetim deposu üretir.
func NewMemoryAuditRepo() *MemoryAuditRepo {
	return &MemoryAuditRepo{ids: map[string]struct{}{}}
}

func (r *MemoryAuditRepo) Append(_ context.Context, e *AuditEntry) error {
	if e == nil {
		return errors.New("claim: denetim kaydı boş")
	}
	if err := e.Validate(); err != nil {
		return err
	}
	r.mu.Lock()
	defer r.mu.Unlock()
	if _, dup := r.ids[e.ID]; dup {
		return ErrDuplicate
	}
	r.ids[e.ID] = struct{}{}
	r.entries = append(r.entries, e.Clone())
	return nil
}

func (r *MemoryAuditRepo) list(pred func(AuditEntry) bool, limit, offset int) []AuditEntry {
	r.mu.RLock()
	out := make([]AuditEntry, 0)
	for _, e := range r.entries {
		if pred(e) {
			out = append(out, e.Clone())
		}
	}
	r.mu.RUnlock()
	sort.SliceStable(out, func(i, j int) bool {
		if !out[i].At.Equal(out[j].At) {
			return out[i].At.After(out[j].At)
		}
		return out[i].ID < out[j].ID
	})
	if offset < 0 {
		offset = 0
	}
	if offset >= len(out) {
		return []AuditEntry{}
	}
	out = out[offset:]
	if limit > 0 && len(out) > limit {
		out = out[:limit]
	}
	return out
}

func (r *MemoryAuditRepo) ListByClaim(_ context.Context, claimID string, limit, offset int) ([]AuditEntry, error) {
	return r.list(func(e AuditEntry) bool { return e.ClaimID == claimID }, limit, offset), nil
}

func (r *MemoryAuditRepo) ListByActor(_ context.Context, by string, limit, offset int) ([]AuditEntry, error) {
	return r.list(func(e AuditEntry) bool { return e.By == by }, limit, offset), nil
}
