package storage

import (
	"context"
	"time"
)

// TimeseriesPoint holds mail count for a single day.
type TimeseriesPoint struct {
	Day   string `json:"day"`   // "2026-04-05"
	Count int64  `json:"count"`
}

// MailTimeseries returns daily mail counts for the last `days` days.
// Tenant-scoped when tenantID is non-nil.
func (s *Store) MailTimeseries(ctx context.Context, days int, tenantID *int64) ([]TimeseriesPoint, error) {
	if s.db == nil {
		return nil, nil
	}
	if days <= 0 {
		days = 30
	}

	var rows interface {
		Next() bool
		Scan(dest ...interface{}) error
		Close()
	}
	var err error

	if tenantID != nil {
		rows, err = s.db.Query(ctx, `
			SELECT date_trunc('day', e.received_at AT TIME ZONE 'UTC')::date AS day,
			       COUNT(*) AS cnt
			FROM emails e
			JOIN email_refs r ON r.email_id = e.id AND r.tenant_id = $2
			WHERE e.received_at >= NOW() - ($1 || ' days')::interval
			GROUP BY day
			ORDER BY day ASC
		`, days, *tenantID)
	} else {
		rows, err = s.db.Query(ctx, `
			SELECT date_trunc('day', received_at AT TIME ZONE 'UTC')::date AS day,
			       COUNT(*) AS cnt
			FROM emails
			WHERE received_at >= NOW() - ($1 || ' days')::interval
			GROUP BY day
			ORDER BY day ASC
		`, days)
	}
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	// Build a map day→count then fill in zeros for missing days
	counts := map[string]int64{}
	for rows.Next() {
		var day time.Time
		var cnt int64
		if err := rows.Scan(&day, &cnt); err == nil {
			counts[day.Format("2006-01-02")] = cnt
		}
	}

	result := make([]TimeseriesPoint, days)
	for i := range result {
		d := time.Now().UTC().AddDate(0, 0, -(days-1-i))
		key := d.Format("2006-01-02")
		result[i] = TimeseriesPoint{Day: key, Count: counts[key]}
	}
	return result, nil
}

// MailActivity holds mail counts over different time windows.
type MailActivity struct {
	Last60Min int64 `json:"last_60_min"`
	Last24h   int64 `json:"last_24h"`
	Last7d    int64 `json:"last_7d"`
	Last30d   int64 `json:"last_30d"`
}

// StorageEstimate holds a rough prediction of when disk space will run out.
type StorageEstimate struct {
	AvgMailsPerDay  float64 `json:"avg_mails_per_day"`
	AvgMailBytes    int64   `json:"avg_mail_bytes"`
	DaysUntilFull   int64   `json:"days_until_full"`   // -1 = unknown/infinite
	ArchiveAgeDays  int64   `json:"archive_age_days"`
}

// MailActivityStats returns how many mails arrived in the last 60min / 24h / 7d / 30d.
func (s *Store) MailActivityStats(ctx context.Context) (*MailActivity, error) {
	if s.db == nil {
		return &MailActivity{}, nil
	}
	now := time.Now()
	windows := []time.Duration{
		60 * time.Minute,
		24 * time.Hour,
		7 * 24 * time.Hour,
		30 * 24 * time.Hour,
	}
	counts := make([]int64, len(windows))
	for i, w := range windows {
		cutoff := now.Add(-w)
		row := s.db.QueryRow(ctx,
			`SELECT COUNT(*) FROM emails WHERE received_at >= $1`, cutoff)
		_ = row.Scan(&counts[i])
	}
	return &MailActivity{
		Last60Min: counts[0],
		Last24h:   counts[1],
		Last7d:    counts[2],
		Last30d:   counts[3],
	}, nil
}

// StorageEstimateStats calculates avg mails/day and avg mail size from the DB.
// freeDiskBytes: free bytes on the archive partition (passed in from caller).
func (s *Store) StorageEstimateStats(ctx context.Context, freeDiskBytes uint64) (*StorageEstimate, error) {
	if s.db == nil {
		return &StorageEstimate{DaysUntilFull: -1}, nil
	}

	var totalMails, totalBytes int64
	var firstReceived *time.Time
	if err := s.db.QueryRow(ctx,
		`SELECT COUNT(*), COALESCE(SUM(size_bytes), 0), MIN(received_at) FROM emails`,
	).Scan(&totalMails, &totalBytes, &firstReceived); err != nil || totalMails == 0 {
		return &StorageEstimate{DaysUntilFull: -1}, nil
	}

	ageDays := int64(1)
	if firstReceived != nil {
		d := time.Since(*firstReceived).Hours() / 24
		if d > 1 {
			ageDays = int64(d)
		}
	}

	avgMailsPerDay := float64(totalMails) / float64(ageDays)
	avgMailBytes := totalBytes / totalMails

	daysUntilFull := int64(-1)
	if avgMailsPerDay > 0 && avgMailBytes > 0 && freeDiskBytes > 0 {
		daysUntilFull = int64(float64(freeDiskBytes) / (avgMailsPerDay * float64(avgMailBytes)))
	}

	return &StorageEstimate{
		AvgMailsPerDay: avgMailsPerDay,
		AvgMailBytes:   avgMailBytes,
		DaysUntilFull:  daysUntilFull,
		ArchiveAgeDays: ageDays,
	}, nil
}
