package services

import (
	"context"
	"log"
	"time"

	"gitea.hostxtra.co.uk/mrhid6/vantage/server/internal/db"
	"gitea.hostxtra.co.uk/mrhid6/vantage/shared/license"
	"go.mongodb.org/mongo-driver/v2/bson"
)

// StartAuditSweeper trims audit logs past their licensed retention.
//
// An immediate pass then daily, following StartLogSweeper's shape. Daily rather
// than hourly because the unit of retention is a day: sweeping twenty-four times
// to delete the same nothing is load without a purpose.
// It takes a context because it runs under leader election: several replicas
// all trimming the same audit logs is duplicated deletion of customer data, and
// the loop must stop the moment this process stops being the leader.
func StartAuditSweeper(ctx context.Context) {
	go func() {
		sweepAuditLogs()
		t := time.NewTicker(24 * time.Hour)
		defer t.Stop()
		for {
			select {
			case <-ctx.Done():
				return
			case <-t.C:
				sweepAuditLogs()
			}
		}
	}()
}

// sweepAuditLogs deletes entries older than each instance's licensed retention.
//
// This is the only part of this subsystem that deletes customer data, so it is
// deliberately conservative in three ways.
//
// It reads the CURRENT licence each run rather than caching a value, so raising
// a customer's retention takes effect on the next sweep instead of whenever a
// process restarts.
//
// It skips an instance whose licence is not valid. A lapsed instance must not
// have its history trimmed on the expired term's allowance — expiry degrades to
// read-only, and deleting more of somebody's audit trail is not read-only.
//
// It skips Unlimited and any non-positive value. A licence that decodes as zero
// has already been filled from the plan base at the decode site, so a zero here
// means something is wrong and doing nothing is the right response to that.
func sweepAuditLogs() {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
	defer cancel()

	var ids []string
	if err := db.Col("instances").Distinct(ctx, "instance_id", bson.M{}).Decode(&ids); err != nil {
		log.Printf("audit sweep: list instances: %v", err)
		return
	}

	for _, id := range ids {
		st := GetLicenseState(id)
		if !st.Active() {
			continue
		}
		days := st.Limits.AuditRetentionDays
		if days == license.Unlimited || days <= 0 {
			continue
		}
		cutoff := time.Now().UTC().AddDate(0, 0, -days)
		res, err := db.Col("audit_logs").DeleteMany(ctx, bson.M{
			"instance_id": id,
			"created_at":  bson.M{"$lt": cutoff},
		})
		if err != nil {
			log.Printf("audit sweep: instance %s: %v", id, err)
			continue
		}
		if res.DeletedCount > 0 {
			log.Printf("audit sweep: instance %s: removed %d entries older than %d days",
				id, res.DeletedCount, days)
		}
	}
}
