package store

import (
	"fmt"
	"math"
	"time"
)

// StatsWindowDays is the rolling window for send statistics on the domain page.
const StatsWindowDays = 30

// SendStats holds message-volume metrics over a stats window (guide § Rate
// limiting — same counting rules as CountMessages).
type SendStats struct {
	Total       int64
	PeakPerHour int64
	AvgPerHour  float64
	WindowDays  int   // actual days queried (min(30, retention))
	WindowHours float64 // hours used for the average-rate denominator
}

// StatsWindow computes the since timestamp and hours denominator for send
// statistics. The query window is min(30, retention) days; hours_in_window is
// min(720, entity age in hours, retention hours).
func StatsWindow(retentionDays int, entityCreated time.Time) (since time.Time, hours float64, windowDays int) {
	if retentionDays <= 0 {
		retentionDays = SendLogRetentionDaysDefault
	}
	windowDays = StatsWindowDays
	if retentionDays < StatsWindowDays {
		windowDays = retentionDays
	}
	since = time.Now().UTC().AddDate(0, 0, -windowDays)

	retentionHours := float64(retentionDays) * 24
	ageHours := time.Since(entityCreated).Hours()
	if ageHours < 0 {
		ageHours = 0
	}

	hours = math.Min(720, math.Min(ageHours, retentionHours))
	if hours < 1 {
		hours = 1
	}
	return since, hours, windowDays
}

// DomainSendStats returns 30-day (or shorter when retention is lower) send
// statistics for a domain name.
func (s *Store) DomainSendStats(name string, retentionDays int, createdAt time.Time) (SendStats, error) {
	since, hours, windowDays := StatsWindow(retentionDays, createdAt)
	stats, err := s.sendStats("domain", name, since, hours)
	if err != nil {
		return SendStats{}, err
	}
	stats.WindowDays = windowDays
	stats.WindowHours = hours
	return stats, nil
}

// AppSendStats returns send statistics for an application login.
func (s *Store) AppSendStats(login string, retentionDays int, createdAt time.Time) (SendStats, error) {
	since, hours, windowDays := StatsWindow(retentionDays, createdAt)
	stats, err := s.sendStats("app_login", login, since, hours)
	if err != nil {
		return SendStats{}, err
	}
	stats.WindowDays = windowDays
	stats.WindowHours = hours
	return stats, nil
}

func (s *Store) sendStats(column, ref string, since time.Time, hours float64) (SendStats, error) {
	sinceStr := since.UTC().Format(time.RFC3339)

	var total int64
	err := s.db.QueryRow(
		`SELECT COUNT(DISTINCT queue_id) FROM send_log
		 WHERE `+column+` = ? AND status != ? AND created_at >= ?`,
		ref, StatusRejected, sinceStr,
	).Scan(&total)
	if err != nil {
		return SendStats{}, fmt.Errorf("send stats total %s %q: %w", column, ref, err)
	}

	var peak int64
	err = s.db.QueryRow(
		`SELECT COALESCE(MAX(bucket_count), 0) FROM (
		   SELECT COUNT(DISTINCT queue_id) AS bucket_count FROM send_log
		   WHERE `+column+` = ? AND status != ? AND created_at >= ?
		   GROUP BY substr(created_at, 1, 13)
		 )`,
		ref, StatusRejected, sinceStr,
	).Scan(&peak)
	if err != nil {
		return SendStats{}, fmt.Errorf("send stats peak %s %q: %w", column, ref, err)
	}

	avg := float64(total) / hours
	return SendStats{Total: total, PeakPerHour: peak, AvgPerHour: avg}, nil
}
