package main

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

	"archivmail/config"
	"archivmail/internal/audit"
	"archivmail/internal/reconciliation"
)

// runReconcile computes the daily completeness reconciliation report (PROJ-52).
// It is designed to be driven by cron once per day (e.g. shortly after
// midnight) and, by default, reconciles the *previous* full calendar day so a
// day is only counted once it is complete.
//
// Usage:
//
//	archivmail reconcile --config /etc/archivmail/config.yml
//	archivmail reconcile --date 2026-07-01
//	archivmail reconcile --days 7          # backfill: reconcile the last 7 days
func runReconcile(args []string) {
	fs := flag.NewFlagSet("reconcile", flag.ExitOnError)
	configPath := fs.String("config", "/etc/archivmail/config.yml", "path to config file")
	dateFlag := fs.String("date", "", "day to reconcile (YYYY-MM-DD, UTC); default: yesterday")
	daysFlag := fs.Int("days", 1, "number of days to reconcile, ending at --date (backfill)")
	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)
	}

	// Determine the target (end) date.
	var end time.Time
	if *dateFlag != "" {
		end, err = time.ParseInLocation("2006-01-02", *dateFlag, time.UTC)
		if err != nil {
			logger.Error("invalid --date (expected YYYY-MM-DD)", "err", err)
			os.Exit(1)
		}
	} else {
		end = time.Now().UTC().AddDate(0, 0, -1).Truncate(24 * time.Hour)
	}

	days := *daysFlag
	if days < 1 {
		days = 1
	}

	dsn := cfg.Database.DSN()
	reconStore, err := reconciliation.New(dsn, logger)
	if err != nil {
		logger.Error("reconciliation store init failed", "err", err)
		os.Exit(1)
	}
	defer reconStore.Close()

	// Wire audit logging so anomalies are persisted as tenant-visible entries.
	audlog, err := audit.New(dsn, cfg.Audit.ResolvedLogPath(), logger)
	if err != nil {
		logger.Warn("audit init failed — anomalies will only be logged", "err", err)
	} else {
		defer audlog.Close()
		reconStore.SetAuditLogger(audlog)
	}

	thresholdPct := cfg.Reconciliation.ResolvedThresholdPct()
	ctx := context.Background()

	totalAnomalies := 0
	// Reconcile oldest → newest so trailing-average history is populated in order.
	for i := days - 1; i >= 0; i-- {
		day := end.AddDate(0, 0, -i)
		anomalies, err := reconStore.ComputeForDate(ctx, day, thresholdPct)
		if err != nil {
			// Per the spec: on failure the day is left WITHOUT a report row
			// (no false zeros). Exit non-zero so cron surfaces the failure.
			logger.Error("reconcile: compute failed", "date", day.Format("2006-01-02"), "err", err)
			os.Exit(1)
		}
		totalAnomalies += len(anomalies)
		logger.Info("reconcile: day complete",
			"date", day.Format("2006-01-02"), "anomalies", len(anomalies))
	}

	logger.Info("reconcile: complete", "days", days, "threshold_pct", thresholdPct,
		"anomalies_total", totalAnomalies)
}
