package index

import (
	"fmt"
	"os"
	"path/filepath"
	"sync"
)

// TenantIndexManager manages a pool of single-Indexer instances, one per tenant,
// for filesystem-backed backends created via New().
// Tenant 0 / nil maps to the global index used by superadmin and as a fallback.
type TenantIndexManager struct {
	basePath  string
	batchSize int
	backend   string
	mu        sync.RWMutex
	pool      map[int64]Indexer // tenant_id -> Indexer
	global    Indexer           // tenant_id == 0 / nil (superadmin, fallback)
}

// NewTenantIndexManager creates a new TenantIndexManager.
// The global index lives at basePath directly; per-tenant indexes at basePath/tenant-<id>/.
func NewTenantIndexManager(basePath string, batchSize int, backend string) (*TenantIndexManager, error) {
	// Ensure the base path directory exists.
	if err := os.MkdirAll(basePath, 0o750); err != nil {
		return nil, fmt.Errorf("tenant index manager: mkdir base: %w", err)
	}

	global, err := New(basePath, batchSize, backend)
	if err != nil {
		return nil, fmt.Errorf("tenant index manager: open global: %w", err)
	}

	return &TenantIndexManager{
		basePath:  basePath,
		batchSize: batchSize,
		backend:   backend,
		pool:      make(map[int64]Indexer),
		global:    global,
	}, nil
}

// ForTenant returns the Indexer for the given tenant.
// nil or pointer to 0 returns the global index.
// Other tenant IDs lazily open a per-tenant index at basePath/tenant-<id>/.
func (m *TenantIndexManager) ForTenant(tenantID *int64) Indexer {
	if tenantID == nil || *tenantID == 0 {
		return m.global
	}
	id := *tenantID

	// Fast path: read lock.
	m.mu.RLock()
	if idx, ok := m.pool[id]; ok {
		m.mu.RUnlock()
		return idx
	}
	m.mu.RUnlock()

	// Slow path: write lock, create if not exists.
	m.mu.Lock()
	defer m.mu.Unlock()

	// Double check after acquiring write lock.
	if idx, ok := m.pool[id]; ok {
		return idx
	}

	dir := filepath.Join(m.basePath, fmt.Sprintf("tenant-%d", id))
	if err := os.MkdirAll(dir, 0o750); err != nil {
		// Return global as fallback on error.
		return m.global
	}

	idx, err := New(dir, m.batchSize, m.backend)
	if err != nil {
		// Return global as fallback on error.
		return m.global
	}

	m.pool[id] = idx
	return idx
}

// Global returns the global (non-tenant) index.
func (m *TenantIndexManager) Global() Indexer {
	return m.global
}

// Close closes all indexes (global + per-tenant).
func (m *TenantIndexManager) Close() error {
	m.mu.Lock()
	defer m.mu.Unlock()

	var firstErr error
	for id, idx := range m.pool {
		if err := idx.Close(); err != nil && firstErr == nil {
			firstErr = fmt.Errorf("close tenant-%d index: %w", id, err)
		}
		delete(m.pool, id)
	}

	if m.global != nil {
		if err := m.global.Close(); err != nil && firstErr == nil {
			firstErr = fmt.Errorf("close global index: %w", err)
		}
	}

	return firstErr
}
