package pdks

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

// MemoryRepo, PDKS icin bellek ici depodur (testler ve MongoDB yokken).
// Eszamanli kullanima guvenlidir; cikis/katis derin kopya alir.
type MemoryRepo struct {
	mu     sync.RWMutex
	depts  map[string]Department
	emps   map[string]Employee
	shifts map[string]Shift
	att    map[string]Attendance
	leaves map[string]Leave
	now    func() time.Time
}

var _ Repository = (*MemoryRepo)(nil)

// NewMemoryRepo bos bir bellek ici depo uretir.
func NewMemoryRepo() *MemoryRepo {
	return &MemoryRepo{
		depts:  map[string]Department{},
		emps:   map[string]Employee{},
		shifts: map[string]Shift{},
		att:    map[string]Attendance{},
		leaves: map[string]Leave{},
		now:    func() time.Time { return time.Now().UTC() },
	}
}

// SetNow, saat kaynagini degistirir (testlerde sabitler).
func (r *MemoryRepo) SetNow(f func() time.Time) { r.now = f }

// Now, depo saat kaynagini dondurur.
func (r *MemoryRepo) Now() time.Time { return r.now() }

// ---- bolum ----

func (r *MemoryRepo) CreateDepartment(_ context.Context, d Department) (Department, error) {
	r.mu.Lock()
	defer r.mu.Unlock()
	if d.ID == "" {
		return d, ErrInvalid
	}
	if _, ok := r.depts[d.ID]; ok {
		return d, ErrDuplicate
	}
	r.depts[d.ID] = d.Clone()
	return d.Clone(), nil
}

func (r *MemoryRepo) UpdateDepartment(_ context.Context, d Department) (Department, error) {
	r.mu.Lock()
	defer r.mu.Unlock()
	if _, ok := r.depts[d.ID]; !ok {
		return d, ErrNotFound
	}
	r.depts[d.ID] = d.Clone()
	return d.Clone(), nil
}

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

func (r *MemoryRepo) GetDepartment(_ context.Context, id string) (Department, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	d, ok := r.depts[id]
	if !ok {
		return d, ErrNotFound
	}
	return d.Clone(), nil
}

func (r *MemoryRepo) ListDepartments(_ context.Context) ([]Department, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	out := make([]Department, 0, len(r.depts))
	for _, d := range r.depts {
		out = append(out, d.Clone())
	}
	// Ad'a gore kararli sira: arayuz ve testler bunu varsayar.
	sort.Slice(out, func(i, j int) bool { return out[i].Name < out[j].Name })
	return out, nil
}

// ---- personel ----

func (r *MemoryRepo) CreateEmployee(_ context.Context, e Employee) (Employee, error) {
	r.mu.Lock()
	defer r.mu.Unlock()
	if e.ID == "" {
		return e, ErrInvalid
	}
	if _, ok := r.emps[e.ID]; ok {
		return e, ErrDuplicate
	}
	r.emps[e.ID] = e.Clone()
	return e.Clone(), nil
}

func (r *MemoryRepo) UpdateEmployee(_ context.Context, e Employee) (Employee, error) {
	r.mu.Lock()
	defer r.mu.Unlock()
	if _, ok := r.emps[e.ID]; !ok {
		return e, ErrNotFound
	}
	r.emps[e.ID] = e.Clone()
	return e.Clone(), nil
}

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

func (r *MemoryRepo) GetEmployee(_ context.Context, id string) (Employee, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	e, ok := r.emps[id]
	if !ok {
		return e, ErrNotFound
	}
	return e.Clone(), nil
}

func (r *MemoryRepo) GetEmployeeByUser(_ context.Context, userID string) (Employee, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	u := strings.TrimSpace(userID)
	for _, e := range r.emps {
		if e.UserID == u {
			return e.Clone(), nil
		}
	}
	return Employee{}, ErrNotFound
}

func (r *MemoryRepo) ListEmployees(_ context.Context) ([]Employee, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	out := make([]Employee, 0, len(r.emps))
	for _, e := range r.emps {
		out = append(out, e.Clone())
	}
	sort.Slice(out, func(i, j int) bool { return out[i].Name < out[j].Name })
	return out, nil
}

// ---- vardiya ----

func (r *MemoryRepo) CreateShift(_ context.Context, s Shift) (Shift, error) {
	r.mu.Lock()
	defer r.mu.Unlock()
	if s.ID == "" {
		return s, ErrInvalid
	}
	if _, ok := r.shifts[s.ID]; ok {
		return s, ErrDuplicate
	}
	r.shifts[s.ID] = s.Clone()
	return s.Clone(), nil
}

func (r *MemoryRepo) UpdateShift(_ context.Context, s Shift) (Shift, error) {
	r.mu.Lock()
	defer r.mu.Unlock()
	if _, ok := r.shifts[s.ID]; !ok {
		return s, ErrNotFound
	}
	r.shifts[s.ID] = s.Clone()
	return s.Clone(), nil
}

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

func (r *MemoryRepo) ListShifts(_ context.Context) ([]Shift, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	out := make([]Shift, 0, len(r.shifts))
	for _, s := range r.shifts {
		out = append(out, s.Clone())
	}
	sort.Slice(out, func(i, j int) bool { return out[i].Start < out[j].Start })
	return out, nil
}

// ---- devam olaylari ----

func (r *MemoryRepo) CreateAttendance(_ context.Context, a Attendance) (Attendance, error) {
	r.mu.Lock()
	defer r.mu.Unlock()
	if a.ID == "" {
		return a, ErrInvalid
	}
	if _, ok := r.att[a.ID]; ok {
		return a, ErrDuplicate
	}
	r.att[a.ID] = a.Clone()
	return a.Clone(), nil
}

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

func (r *MemoryRepo) GetAttendance(_ context.Context, id string) (Attendance, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	a, ok := r.att[id]
	if !ok {
		return a, ErrNotFound
	}
	return a.Clone(), nil
}

// ListAttendance olaylari ZAMAN SIRASIYLA dondurur: sure hesabi bu siraya
// dayanir, sira bozulursa gunluk toplam yanlis cikar (sessiz hata).
func (r *MemoryRepo) ListAttendance(_ context.Context, f AttendFilter) ([]Attendance, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	out := make([]Attendance, 0, len(r.att))
	for _, a := range r.att {
		if f.EmpID != "" && a.EmpID != f.EmpID {
			continue
		}
		if f.Type != "" && a.Type != f.Type {
			continue
		}
		// Aralik: DayOf uzerinden karsilastirilir (string karsilastirmasi
		// "2006-01-02" biciminde kronolojiktir).
		if f.From != "" && a.DayOf < f.From {
			continue
		}
		if f.To != "" && a.DayOf > f.To {
			continue
		}
		out = append(out, a.Clone())
	}
	sort.Slice(out, func(i, j int) bool {
		if out[i].At.Equal(out[j].At) {
			return out[i].ID < out[j].ID // ayni anda: kararli sira
		}
		return out[i].At.Before(out[j].At)
	})
	if f.Limit > 0 && len(out) > f.Limit {
		out = out[:f.Limit]
	}
	return out, nil
}

// ---- izin ----

func (r *MemoryRepo) CreateLeave(_ context.Context, l Leave) (Leave, error) {
	r.mu.Lock()
	defer r.mu.Unlock()
	if l.ID == "" {
		return l, ErrInvalid
	}
	if _, ok := r.leaves[l.ID]; ok {
		return l, ErrDuplicate
	}
	r.leaves[l.ID] = l.Clone()
	return l.Clone(), nil
}

func (r *MemoryRepo) UpdateLeave(_ context.Context, l Leave) (Leave, error) {
	r.mu.Lock()
	defer r.mu.Unlock()
	if _, ok := r.leaves[l.ID]; !ok {
		return l, ErrNotFound
	}
	r.leaves[l.ID] = l.Clone()
	return l.Clone(), nil
}

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

func (r *MemoryRepo) GetLeave(_ context.Context, id string) (Leave, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	l, ok := r.leaves[id]
	if !ok {
		return l, ErrNotFound
	}
	return l.Clone(), nil
}

// ListLeave, aralikla KESISEN izinleri dondurur: izin gunleri rapor araliginin
// ICINDE olmali (tam eslesmesi gerekmez — 20-25 arasi bir izin 22-28'lik
// rapora da girer).
func (r *MemoryRepo) ListLeave(_ context.Context, empID, from, to string) ([]Leave, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()
	out := make([]Leave, 0, len(r.leaves))
	for _, l := range r.leaves {
		if empID != "" && l.EmpID != empID {
			continue
		}
		if to != "" && l.From > to {
			continue
		}
		if from != "" && l.To < from {
			continue
		}
		out = append(out, l.Clone())
	}
	sort.Slice(out, func(i, j int) bool { return out[i].From < out[j].From })
	return out, nil
}
