package auditlog

import (
	"context"
	"sort"
	"sync"
)

// Logger is the interface for recording and querying audit log entries.
// Log() must be non-blocking (backed by a buffered channel or in-memory store).
type Logger interface {
	Log(entry AuditEntry)
	Query(ctx context.Context, q AuditQuery) (*AuditResult, error)
	QueryCosts(ctx context.Context, q CostQuery) (*CostResult, error)
	Start()
	Stop()
}

// ─── MemLogger ────────────────────────────────────────────────────────────────

// MemLogger is a thread-safe in-memory Logger used in tests.
// It stores entries in insertion order and supports basic filtering.
type MemLogger struct {
	mu      sync.RWMutex
	entries []AuditEntry
}

// NewMemLogger creates a new MemLogger.
func NewMemLogger() *MemLogger { return &MemLogger{} }

func (m *MemLogger) Log(e AuditEntry) {
	m.mu.Lock()
	m.entries = append(m.entries, e)
	m.mu.Unlock()
}

// Entries returns a copy of all stored entries (safe to call from tests).
func (m *MemLogger) Entries() []AuditEntry {
	m.mu.RLock()
	defer m.mu.RUnlock()
	out := make([]AuditEntry, len(m.entries))
	copy(out, m.entries)
	return out
}

func (m *MemLogger) Query(_ context.Context, q AuditQuery) (*AuditResult, error) {
	m.mu.RLock()
	defer m.mu.RUnlock()

	sensitivityOrder := map[string]int{
		"none": 0, "low": 1, "medium": 2, "high": 3, "critical": 4,
	}
	minLevel := sensitivityOrder[q.MinSensitivity]

	var filtered []AuditEntry
	for _, e := range m.entries {
		if e.TenantID != q.TenantID {
			continue
		}
		if q.UserID != "" && e.UserID != q.UserID {
			continue
		}
		if !q.StartTime.IsZero() && e.Timestamp.Before(q.StartTime) {
			continue
		}
		if !q.EndTime.IsZero() && e.Timestamp.After(q.EndTime) {
			continue
		}
		if q.Provider != "" && e.Provider != q.Provider {
			continue
		}
		if q.MinSensitivity != "" {
			if sensitivityOrder[e.SensitivityLevel] < minLevel {
				continue
			}
		}
		filtered = append(filtered, e)
	}

	total := len(filtered)
	if q.Offset < len(filtered) {
		filtered = filtered[q.Offset:]
	} else {
		filtered = nil
	}
	limit := q.Limit
	if limit <= 0 || limit > 200 {
		limit = 50
	}
	if len(filtered) > limit {
		filtered = filtered[:limit]
	}

	return &AuditResult{Data: filtered, Total: total}, nil
}

func (m *MemLogger) QueryCosts(_ context.Context, q CostQuery) (*CostResult, error) {
	m.mu.RLock()
	defer m.mu.RUnlock()

	type aggKey = string
	type agg struct {
		tokens int
		cost   float64
		count  int
	}
	totals := map[aggKey]*agg{}

	for _, e := range m.entries {
		if e.TenantID != q.TenantID {
			continue
		}
		if !q.StartTime.IsZero() && e.Timestamp.Before(q.StartTime) {
			continue
		}
		if !q.EndTime.IsZero() && e.Timestamp.After(q.EndTime) {
			continue
		}
		var key string
		switch q.GroupBy {
		case "model":
			key = e.ModelUsed
		case "department":
			key = e.Department
		default:
			key = e.Provider
		}
		if totals[key] == nil {
			totals[key] = &agg{}
		}
		totals[key].tokens += e.TokenTotal
		totals[key].cost += e.CostUSD
		totals[key].count++
	}

	var data []CostSummary
	for k, v := range totals {
		data = append(data, CostSummary{
			Key:          k,
			TotalTokens:  v.tokens,
			TotalCostUSD: v.cost,
			RequestCount: v.count,
		})
	}
	sort.Slice(data, func(i, j int) bool { return data[i].Key < data[j].Key })
	return &CostResult{Data: data}, nil
}

func (m *MemLogger) Start() {}
func (m *MemLogger) Stop()  {}
