package sync

import (
	"context"
	"time"

	"github.com/stacklok/toolhive-registry-server/internal/config"
	"github.com/stacklok/toolhive-registry-server/internal/sources"
	"github.com/stacklok/toolhive-registry-server/internal/status"
)

// defaultDataChangeDetector implements DataChangeDetector
type defaultDataChangeDetector struct {
	registryHandlerFactory sources.RegistryHandlerFactory
}

// IsDataChanged checks if source data has changed by comparing hashes for a specific registry.
// Returns the fetched data so PerformSync can reuse it without a second fetch.
func (d *defaultDataChangeDetector) IsDataChanged(
	ctx context.Context, regCfg *config.SourceConfig, syncStatus *status.SyncStatus,
) (bool, *sources.FetchResult, error) {
	// Check for hash in syncStatus first, then fallback
	var lastSyncHash string
	if syncStatus != nil {
		lastSyncHash = syncStatus.LastSyncHash
	}

	// Get registry handler
	registryHandler, err := d.registryHandlerFactory.CreateHandler(regCfg)
	if err != nil {
		return true, nil, err
	}

	// Fetch current data from source — the result is returned so PerformSync can reuse it
	fetchResult, err := registryHandler.FetchRegistry(ctx, regCfg)
	if err != nil {
		return true, nil, err
	}

	// If we don't have a last sync hash, consider data changed
	if lastSyncHash == "" {
		return true, fetchResult, nil
	}

	// Compare hashes - data changed if different
	return fetchResult.Hash != lastSyncHash, fetchResult, nil
}

// defaultAutomaticSyncChecker implements AutomaticSyncChecker
type defaultAutomaticSyncChecker struct{}

// IsIntervalSyncNeeded checks if sync is needed based on time interval for a specific registry.
// The sync schedule is read from the stored SyncStatus rather than from configuration.
// This ensures the runtime sync logic uses the schedule stored in DB/filesystem during initialization.
// Returns: (syncNeeded, nextSyncTime, error)
// nextSyncTime is a future time when the next sync should occur, or zero time if no schedule configured
func (*defaultAutomaticSyncChecker) IsIntervalSyncNeeded(
	_ *config.SourceConfig, syncStatus *status.SyncStatus,
) (bool, time.Time, error) {
	// Read sync schedule from stored state instead of config
	// Non-synced registries (managed, kubernetes) will have an empty SyncSchedule
	if syncStatus == nil || syncStatus.SyncSchedule == "" {
		return false, time.Time{}, nil
	}

	// Parse the sync interval from stored state
	interval, err := time.ParseDuration(syncStatus.SyncSchedule)
	if err != nil {
		return false, time.Time{}, err
	}

	now := time.Now()

	// Check for last sync time in syncStatus
	lastSyncTime := syncStatus.LastAttempt

	// If we don't have a last sync time, sync is needed
	if lastSyncTime == nil {
		return true, now.Add(interval), nil
	}

	// Calculate when next sync should happen based on last sync
	nextSyncTime := lastSyncTime.Add(interval)

	// Check if it's time for the next sync
	syncNeeded := now.After(nextSyncTime) || now.Equal(nextSyncTime)

	if syncNeeded {
		// If sync is needed now, calculate when the next one after this should be
		return true, now.Add(interval), nil
	}

	// Sync not needed yet, return the originally calculated next sync time
	return false, nextSyncTime, nil
}
