package retention

import (
	"context"
	"fmt"
	"log"
	"sync"
	"time"

	"chat-switchboard/storage"
	"chat-switchboard/store"
)

const DefaultInterval = 1 * time.Hour

// ScannerConfig holds startup configuration.
type ScannerConfig struct {
	Interval time.Duration
}

// Scanner periodically purges channels whose purge_after timestamp has passed.
// Channels with global/team provider scopes are archived with a TTL by
// DeleteChannel; this scanner performs the deferred hard-delete.
type Scanner struct {
	stores   store.Stores
	objStore storage.ObjectStore

	wg     sync.WaitGroup
	stopCh chan struct{}

	interval time.Duration
}

// NewScanner creates a retention scanner. Call Start() to begin.
func NewScanner(stores store.Stores, objStore storage.ObjectStore, cfg ScannerConfig) *Scanner {
	interval := cfg.Interval
	if interval <= 0 {
		interval = DefaultInterval
	}
	return &Scanner{
		stores:   stores,
		objStore: objStore,
		stopCh:   make(chan struct{}),
		interval: interval,
	}
}

// Start begins the scan loop in a background goroutine.
func (sc *Scanner) Start() {
	sc.wg.Add(1)
	go func() {
		defer sc.wg.Done()
		sc.loop()
	}()
	log.Printf("[retention] scanner started (interval=%s)", sc.interval)
}

// Stop signals the scanner to stop and waits for in-flight work to drain.
func (sc *Scanner) Stop() {
	close(sc.stopCh)
	sc.wg.Wait()
	log.Printf("[retention] scanner stopped")
}

func (sc *Scanner) loop() {
	ticker := time.NewTicker(sc.interval)
	defer ticker.Stop()

	for {
		select {
		case <-sc.stopCh:
			return
		case <-ticker.C:
			sc.tick()
		}
	}
}

func (sc *Scanner) tick() {
	ctx := context.Background()

	// If TTL is 0 the feature is disabled — nothing to purge
	ttl := sc.retentionTTL(ctx)
	if ttl <= 0 {
		return
	}

	ids, err := sc.stores.Channels.ListPurgeable(ctx)
	if err != nil {
		log.Printf("[retention] ListPurgeable error: %v", err)
		return
	}
	if len(ids) == 0 {
		return
	}

	log.Printf("[retention] purging %d channel(s)", len(ids))

	for _, id := range ids {
		// Clean up storage files first
		if sc.objStore != nil {
			prefix := fmt.Sprintf("files/%s", id)
			if err := sc.objStore.DeletePrefix(ctx, prefix); err != nil {
				log.Printf("[retention] storage cleanup for %s failed: %v", id, err)
			}
		}

		// Hard delete (Purge verifies is_archived)
		if err := sc.stores.Channels.Purge(ctx, id); err != nil {
			log.Printf("[retention] purge %s failed: %v", id, err)
		}
	}
}

func (sc *Scanner) retentionTTL(ctx context.Context) int {
	cfg, err := sc.stores.GlobalConfig.Get(ctx, "retention_ttl_days")
	if err != nil {
		return 0
	}
	if v, ok := cfg["value"].(float64); ok {
		return int(v)
	}
	return 0
}
