package user

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

// Depo hataları: servis katmanı bunları kullanıcıya anlamlı metne çevirir.
var (
	// ErrNotFound kullanıcı bulunamadı (GİRİŞTE "böyle bir kullanıcı yok"
	// demek bilgi sızdırır; bu yüzden giriş hatası ile aynı mesaj verilir).
	ErrNotFound = errors.New("kullanıcı bulunamadı")
	// ErrExists kullanıcı adı zaten alınmış.
	ErrExists = errors.New("kullanıcı adı zaten kullanılıyor")
	// ErrDisabled hesap kapatılmış.
	ErrDisabled = errors.New("hesap devre dışı")
)

// Store, kullanıcı kalıcılığı için gereken asgari işlemler.
//
// Arayüz olarak tanımlanır çünkü bellek içi (test) ve MongoDB (üretim)
// uygulamaları değişir; servis katmanı ikisini de bilmemelidir.
type Store interface {
	Create(ctx context.Context, u *User) error
	ByUsername(ctx context.Context, username string) (*User, error)
	ByID(ctx context.Context, id string) (*User, error)
	List(ctx context.Context, limit int) ([]*User, error)
	// UpdateLastLogin giriş zamanını işaretler (kimlik doğrulama sonrası).
	UpdateLastLogin(ctx context.Context, id string, at int64) error
}

// MemoryStore, testler ve veritabanı yokken kullanılan bellek içi depo.
//
// Kullanıcı adı benzersizliği yazma sırasında kilit ile korunur; aksi hâlde
// iki eşzamanlı kayıt aynı adı iki kez yazabilirdi.
type MemoryStore struct {
	mu    sync.RWMutex
	users map[string]*User // anahtar: normalize edilmiş kullanıcı adı
	byID  map[string]*User
}

// NewMemoryStore boş bir bellek içi depo üretir.
func NewMemoryStore() *MemoryStore {
	return &MemoryStore{
		users: map[string]*User{},
		byID:  map[string]*User{},
	}
}

func (m *MemoryStore) Create(_ context.Context, u *User) error {
	m.mu.Lock()
	defer m.mu.Unlock()
	key := NormalizeUsername(u.Username)
	if _, exists := m.users[key]; exists {
		return ErrExists
	}
	// Kopyala: çağıranın nesnesini sonradan değiştirmesi depoyu bozmasın.
	cp := *u
	cp.Username = key
	m.users[key] = &cp
	m.byID[u.ID] = &cp
	return nil
}

func (m *MemoryStore) ByUsername(_ context.Context, username string) (*User, error) {
	m.mu.RLock()
	defer m.mu.RUnlock()
	u, ok := m.users[NormalizeUsername(username)]
	if !ok {
		return nil, ErrNotFound
	}
	cp := *u
	return &cp, nil
}

func (m *MemoryStore) ByID(_ context.Context, id string) (*User, error) {
	m.mu.RLock()
	defer m.mu.RUnlock()
	u, ok := m.byID[id]
	if !ok {
		return nil, ErrNotFound
	}
	cp := *u
	return &cp, nil
}

func (m *MemoryStore) List(_ context.Context, limit int) ([]*User, error) {
	m.mu.RLock()
	defer m.mu.RUnlock()
	out := make([]*User, 0, len(m.users))
	for _, u := range m.users {
		cp := *u
		out = append(out, &cp)
	}
	// Kararlı sıra: ad'a göre. Testler ve arayüz bunu varsayar.
	sort.Slice(out, func(i, j int) bool {
		return strings.ToLower(out[i].Username) < strings.ToLower(out[j].Username)
	})
	if limit > 0 && len(out) > limit {
		out = out[:limit]
	}
	return out, nil
}

func (m *MemoryStore) UpdateLastLogin(_ context.Context, id string, at int64) error {
	m.mu.Lock()
	defer m.mu.Unlock()
	u, ok := m.byID[id]
	if !ok {
		return ErrNotFound
	}
	u.LastLoginAt = unixTime(at)
	return nil
}
