package security

import (
	"context"
	"crypto/x509"
	"errors"
	"fmt"
	"time"

	"git.cloudinit.dev/coreci/orca/internal/store"
)

// RotationWindow is the lead-time before expiry at which we start warning
// the operator. REQ-034 says 30 days.
const RotationWindow = 30 * 24 * time.Hour

// RotationAlarm checks cert's remaining validity. Returns nil if the cert
// has more than RotationWindow of life left. If remaining <= RotationWindow,
// returns a non-nil error wrapping the days-remaining message so callers
// can log it. Callers MUST treat a non-nil result as a warning, not a fatal
// error — the cert is still usable; we want to alert the operator ahead
// of time.
func RotationAlarm(cert *x509.Certificate) error {
	if cert == nil {
		return errors.New("RotationAlarm: nil cert")
	}
	now := time.Now()
	remaining := cert.NotAfter.Sub(now)
	if remaining > RotationWindow {
		return nil
	}
	days := int(remaining.Hours() / 24)
	if days < 0 {
		days = 0
	}
	return fmt.Errorf("cert rotates in %d days (NotAfter=%s) — renew soon (REQ-034)",
		days, cert.NotAfter.UTC().Format(time.RFC3339))
}

// RotationAlarmAt is identical to RotationAlarm but takes an explicit "now"
// for deterministic testing.
func RotationAlarmAt(cert *x509.Certificate, now time.Time) error {
	if cert == nil {
		return errors.New("RotationAlarmAt: nil cert")
	}
	remaining := cert.NotAfter.Sub(now)
	if remaining > RotationWindow {
		return nil
	}
	days := int(remaining.Hours() / 24)
	if days < 0 {
		days = 0
	}
	return fmt.Errorf("cert rotates in %d days (NotAfter=%s) — renew soon (REQ-034)",
		days, cert.NotAfter.UTC().Format(time.RFC3339))
}

// PruneOldCerts deletes all certs for (nodeID, kind) beyond the most recent
// `keep` rows, ordered by created_at DESC. Per REQ-025, the rotation
// history is bounded at 10 generations per cert kind. Returns the number
// of rows deleted.
//
// `keep` is a positive integer; values <= 0 are treated as 10 (the
// documented max).
func PruneOldCerts(ctx context.Context, repo *store.CertRepo, nodeID, kind string, keep int) (int64, error) {
	if repo == nil {
		return 0, errors.New("PruneOldCerts: nil repo")
	}
	if keep <= 0 {
		keep = 10
	}
	return repo.PruneOlderThan(ctx, nodeID, kind, keep)
}
