// Package setupstatus reports whether the application's initial setup has been
// completed. Setup is considered complete when at least one admin user exists
// AND a non-empty base_url has been configured, regardless of how those were
// created (setup wizard, API, or a future CLI). This keeps the setup gate a
// derived property of real data rather than a manually-flipped flag that can
// drift out of sync.
package setupstatus

import (
	"context"
	"sync"
	"time"
)

// AdminCounter is satisfied by *database.Queries. It is defined as an interface
// here so this package does not import the database package, keeping the
// dependency graph flat and avoiding import cycles.
type AdminCounter interface {
	CountAdmins(ctx context.Context) (int64, error)
}

// BaseURLGetter returns the configured base_url value from the database, or an
// error if it cannot be read. Defined as a function type (not an interface) so
// it can be satisfied by a closure wrapping *database.Queries.GetSystemConfig
// without importing the database package.
type BaseURLGetter func(ctx context.Context) (string, error)

var (
	cacheMu       sync.RWMutex
	cacheComplete bool = true
	cacheExpiry   time.Time
	cacheTTL           = 10 * time.Second
)

// IsSetupComplete reports whether setup is complete. Setup is complete when at
// least one admin user exists AND base_url is configured. A short in-memory
// cache avoids hammering the database on every request. On a database error the
// function fails open (returns true) so a transient outage does not lock users
// out of the app.
func IsSetupComplete(ctx context.Context, q AdminCounter, baseURLGetter BaseURLGetter) bool {
	cacheMu.RLock()
	if time.Now().Before(cacheExpiry) {
		complete := cacheComplete
		cacheMu.RUnlock()
		return complete
	}
	cacheMu.RUnlock()

	complete := true

	count, err := q.CountAdmins(ctx)
	if err == nil {
		complete = count > 0
	}

	if complete && baseURLGetter != nil {
		baseURL, err := baseURLGetter(ctx)
		if err == nil {
			complete = baseURL != ""
		}
	}

	cacheMu.Lock()
	cacheComplete = complete
	cacheExpiry = time.Now().Add(cacheTTL)
	cacheMu.Unlock()
	return complete
}

// Invalidate clears the cached setup status so the next call to IsSetupComplete
// re-reads from the database. Call this after any write that could change the
// admin user count (user creation, role promotion/demotion, user deletion) or
// the base_url configuration.
func Invalidate() {
	cacheMu.Lock()
	cacheComplete = true
	cacheExpiry = time.Time{}
	cacheMu.Unlock()
}
