package main

import (
	"context"
	"flag"
	"log/slog"
	"os"

	"archivmail/config"
	"archivmail/internal/audit"
	"archivmail/internal/index"
	"archivmail/internal/storage"
)

// runPurge deletes mails that are BOTH past retain_until AND explicitly
// marked_for_deletion=TRUE by a user in the UI, removes them from the
// search index, and writes one audit log entry per deleted mail
// (GoBD-Nachvollziehbarkeit). Intended to be cron-driven (PROJ-56c).
//
// Deliberately NOT the same query as the manual /api/admin/purge endpoint
// (Store.Purge, deletes everything past retain_until regardless of marking):
// an unattended cron job must never delete mails on date alone — a human
// has to have explicitly flagged each one for deletion first.
//
// Usage: archivmail purge [-config /path/to/config.yml] [-dry-run]
func runPurge(args []string) {
	fs := flag.NewFlagSet("purge", flag.ExitOnError)
	configPath := fs.String("config", "/etc/archivmail/config.yml", "path to config file")
	dryRun := fs.Bool("dry-run", false, "list expired mails without deleting them")
	fs.Parse(args)

	logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))

	cfg, err := config.Load(*configPath)
	if err != nil {
		logger.Error("failed to load config", "err", err)
		os.Exit(1)
	}

	storeCfg := storage.Config{
		Dir:             cfg.Storage.StorePath,
		Keyfile:         cfg.Storage.Keyfile,
		DSN:             cfg.Database.DSN(),
		CompressEnabled: cfg.Storage.Compress,
	}
	mailStore, err := storage.New(storeCfg)
	if err != nil {
		logger.Error("storage init failed", "err", err)
		os.Exit(1)
	}
	defer mailStore.Close()

	ctx := context.Background()

	ids, err := mailStore.ListExpiredMarkedMailIDs(ctx)
	if err != nil {
		logger.Error("purge: list expired+marked failed", "err", err)
		os.Exit(1)
	}
	if len(ids) == 0 {
		logger.Info("purge: nothing to do, no expired+marked mails")
		return
	}

	if *dryRun {
		logger.Info("purge: dry-run, would delete", "count", len(ids))
		for _, id := range ids {
			logger.Info("purge: dry-run candidate", "id", id)
		}
		return
	}

	// Index + audit log are best-effort extras (PROJ-56c); the OCR/index
	// backends and audit DB can be unreachable without that blocking the
	// actual deletion, which is what GoBD-Löschsperre/Retention requires.
	var idxMgr index.TenantIndexer
	indexBackend := cfg.Index.Backend
	if indexBackend == "manticore" {
		dsn := cfg.Index.ManticoreDSN
		if dsn == "" {
			dsn = "manticore@tcp(127.0.0.1:9306)/?charset=utf8mb4"
		}
		if m, err := index.NewManticoreTenantManager(dsn); err == nil {
			idxMgr = m
			defer m.Close()
		} else {
			logger.Warn("purge: index init failed, skipping index cleanup", "err", err)
		}
	}

	var audlog *audit.Logger
	if a, err := audit.New(cfg.Database.DSN(), cfg.Audit.ResolvedLogPath(), logger); err == nil {
		audlog = a
		defer audlog.Close()
	} else {
		logger.Warn("purge: audit log init failed, deletions will not be audited", "err", err)
	}

	deleted := 0
	failed := 0
	for _, id := range ids {
		tenantID, _ := mailStore.GetTenantForMail(ctx, id)

		if err := mailStore.Delete(id); err != nil {
			logger.Warn("purge: delete failed", "id", id, "err", err)
			failed++
			continue
		}

		if idxMgr != nil {
			if err := idxMgr.ForTenant(tenantID).Delete(id); err != nil {
				logger.Warn("purge: index cleanup failed", "id", id, "err", err)
			}
		}

		if audlog != nil {
			audlog.Log(audit.Entry{
				EventType: "mail_purged",
				Username:  "cron:purge",
				TenantID:  tenantID,
				MailID:    id,
				Success:   true,
				Detail:    "Cron-Purge: Aufbewahrungsfrist abgelaufen UND vom Nutzer zur Löschung markiert",
			})
		}

		deleted++
	}

	logger.Info("purge: complete", "total", len(ids), "deleted", deleted, "failed", failed)
}
