package repository

import (
	"context"
	"fmt"
	"sync"

	"apps/backend/internal/module/usage/domain"
)

type MemoryStore struct {
	mu        sync.Mutex
	events    []*domain.Event
	counters  map[string]*domain.MonthlyCounter
	prefs     map[int64]*domain.MemberPrefs
	purchases []*domain.Purchase
}

func NewMemory() *MemoryStore {
	return &MemoryStore{
		counters: map[string]*domain.MonthlyCounter{},
		prefs:    map[int64]*domain.MemberPrefs{},
	}
}

func memoryCounterID(uid int64, monthKey string) string {
	return fmt.Sprintf("%d:%s", uid, monthKey)
}

func (s *MemoryStore) ReservePlatform(_ context.Context, uid int64, monthKey, meter string, cost, totalLimit, meterLimit int) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	id := memoryCounterID(uid, monthKey)
	counter := s.counters[id]
	if counter == nil {
		counter = &domain.MonthlyCounter{ID: id, UID: uid, MonthKey: monthKey, ByMeter: map[string]int{}}
		for _, event := range s.events {
			if event.UID == uid && event.MonthKey == monthKey && event.KeyMode == domain.KeyModePlatform {
				credits := event.Credits
				if minimum := domain.MeterCost(event.Meter); credits < minimum {
					credits = minimum
				}
				counter.TotalCredits += credits
				counter.ByMeter[event.Meter] += credits
			}
		}
		s.counters[id] = counter
	}
	if totalLimit >= 0 && counter.TotalCredits+cost > totalLimit {
		return domain.ErrQuotaExceeded
	}
	if meterLimit >= 0 && counter.ByMeter[meter]+cost > meterLimit {
		return domain.ErrMeterCapExceeded
	}
	counter.TotalCredits += cost
	counter.ByMeter[meter] += cost
	counter.UpdatedAt = domain.NowNano()
	return nil
}

func (s *MemoryStore) ReleasePlatform(_ context.Context, uid int64, monthKey, meter string, cost int) error {
	if cost <= 0 {
		return nil
	}
	s.mu.Lock()
	defer s.mu.Unlock()
	counter := s.counters[memoryCounterID(uid, monthKey)]
	if counter == nil {
		return nil
	}
	if counter.TotalCredits < cost || counter.ByMeter[meter] < cost {
		return nil
	}
	counter.TotalCredits -= cost
	counter.ByMeter[meter] -= cost
	counter.UpdatedAt = domain.NowNano()
	return nil
}

func (s *MemoryStore) GetMonthlyCounter(_ context.Context, uid int64, monthKey string) (*domain.MonthlyCounter, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	counter := s.counters[memoryCounterID(uid, monthKey)]
	if counter == nil {
		return nil, domain.ErrNotFound
	}
	copyCounter := *counter
	copyCounter.ByMeter = make(map[string]int, len(counter.ByMeter))
	for meter, credits := range counter.ByMeter {
		copyCounter.ByMeter[meter] = credits
	}
	return &copyCounter, nil
}

func (s *MemoryStore) InsertEvent(_ context.Context, e *domain.Event) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	cp := *e
	s.events = append(s.events, &cp)
	return nil
}

func (s *MemoryStore) ListEvents(_ context.Context, uid int64, monthKey, keyMode string, limit int) ([]*domain.Event, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	var out []*domain.Event
	for i := len(s.events) - 1; i >= 0; i-- {
		e := s.events[i]
		if e.UID != uid {
			continue
		}
		if monthKey != "" && e.MonthKey != monthKey {
			continue
		}
		if keyMode != "" && keyMode != "all" && e.KeyMode != keyMode {
			continue
		}
		cp := *e
		out = append(out, &cp)
		if limit > 0 && len(out) >= limit {
			break
		}
	}
	return out, nil
}

func (s *MemoryStore) ListEventsAll(_ context.Context, monthKey string, limit int) ([]*domain.Event, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	var out []*domain.Event
	for i := len(s.events) - 1; i >= 0; i-- {
		e := s.events[i]
		if monthKey != "" && e.MonthKey != monthKey {
			continue
		}
		cp := *e
		out = append(out, &cp)
		if limit > 0 && len(out) >= limit {
			break
		}
	}
	return out, nil
}

func (s *MemoryStore) ListEventsRange(_ context.Context, fromNano, toExclusiveNano int64) ([]*domain.Event, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	out := make([]*domain.Event, 0)
	for _, event := range s.events {
		if event.CreatedAt < fromNano || event.CreatedAt >= toExclusiveNano {
			continue
		}
		cp := *event
		out = append(out, &cp)
	}
	return out, nil
}

func (s *MemoryStore) GetPrefs(_ context.Context, uid int64) (*domain.MemberPrefs, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	if p, ok := s.prefs[uid]; ok {
		cp := *p
		return &cp, nil
	}
	return &domain.MemberPrefs{UID: uid, PlanID: domain.PlanFree}, nil
}

func (s *MemoryStore) SavePrefs(_ context.Context, p *domain.MemberPrefs) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	cp := *p
	s.prefs[p.UID] = &cp
	return nil
}

func (s *MemoryStore) InsertPurchase(_ context.Context, p *domain.Purchase) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	cp := *p
	s.purchases = append(s.purchases, &cp)
	return nil
}

func (s *MemoryStore) ListPurchases(_ context.Context, uid int64, limit int) ([]*domain.Purchase, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	var out []*domain.Purchase
	for i := len(s.purchases) - 1; i >= 0; i-- {
		p := s.purchases[i]
		if p.UID != uid {
			continue
		}
		cp := *p
		out = append(out, &cp)
		if limit > 0 && len(out) >= limit {
			break
		}
	}
	return out, nil
}

var _ domain.Repository = (*MemoryStore)(nil)
