package routing

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

// RuleStore is the persistence interface for routing rules.
// The MemStore implements it for tests; PgStore implements it for production.
type RuleStore interface {
	// ListActive returns all enabled rules for tenantID, sorted by priority ASC.
	ListActive(ctx context.Context, tenantID string) ([]RoutingRule, error)
	// Get returns a single rule by id, scoped to tenantID.
	Get(ctx context.Context, id, tenantID string) (RoutingRule, error)
	// Create persists a new rule and returns the created record.
	Create(ctx context.Context, rule RoutingRule) (RoutingRule, error)
	// Update replaces an existing rule and returns the updated record.
	Update(ctx context.Context, rule RoutingRule) (RoutingRule, error)
	// Delete permanently removes a rule. Only affects the given tenantID.
	Delete(ctx context.Context, id, tenantID string) error
}

// ErrNotFound is returned by Get/Update/Delete when the rule does not exist.
var ErrNotFound = fmt.Errorf("routing rule not found")

// ─── MemStore — in-memory implementation (tests & dev) ──────────────────────

// MemStore is a thread-safe in-memory RuleStore for unit tests.
type MemStore struct {
	mu    sync.RWMutex
	rules map[string]RoutingRule // id → rule
	seq   int
}

// NewMemStore creates an empty MemStore.
func NewMemStore() *MemStore {
	return &MemStore{rules: make(map[string]RoutingRule)}
}

func (m *MemStore) ListActive(_ context.Context, tenantID string) ([]RoutingRule, error) {
	m.mu.RLock()
	defer m.mu.RUnlock()

	var out []RoutingRule
	for _, r := range m.rules {
		if r.TenantID == tenantID && r.IsEnabled {
			out = append(out, r)
		}
	}
	sort.Slice(out, func(i, j int) bool { return out[i].Priority < out[j].Priority })
	return out, nil
}

func (m *MemStore) Get(_ context.Context, id, tenantID string) (RoutingRule, error) {
	m.mu.RLock()
	defer m.mu.RUnlock()

	r, ok := m.rules[id]
	if !ok || r.TenantID != tenantID {
		return RoutingRule{}, ErrNotFound
	}
	return r, nil
}

func (m *MemStore) Create(_ context.Context, rule RoutingRule) (RoutingRule, error) {
	m.mu.Lock()
	defer m.mu.Unlock()

	m.seq++
	rule.ID = fmt.Sprintf("mem-%d", m.seq)
	rule.CreatedAt = time.Now()
	rule.UpdatedAt = time.Now()
	m.rules[rule.ID] = rule
	return rule, nil
}

func (m *MemStore) Update(_ context.Context, rule RoutingRule) (RoutingRule, error) {
	m.mu.Lock()
	defer m.mu.Unlock()

	existing, ok := m.rules[rule.ID]
	if !ok || existing.TenantID != rule.TenantID {
		return RoutingRule{}, ErrNotFound
	}
	rule.CreatedAt = existing.CreatedAt
	rule.UpdatedAt = time.Now()
	m.rules[rule.ID] = rule
	return rule, nil
}

func (m *MemStore) Delete(_ context.Context, id, tenantID string) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	r, ok := m.rules[id]
	if !ok || r.TenantID != tenantID {
		return ErrNotFound
	}
	delete(m.rules, id)
	return nil
}
