package store

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"time"
)

// RepoStatsHistoryPoint is one (day, host) point for the trend chart.
// Both metric pointers may be nil — a row exists as soon as either
// metric was reported on that day.
type RepoStatsHistoryPoint struct {
	Day            time.Time // 00:00:00 UTC
	TotalSizeBytes *int64
	SnapshotCount  *int64
}

// UpsertHostRepoStatsHistory records the metrics carried by a
// repo.stats patch into the daily history table. Only the non-nil
// fields of patch.TotalSizeBytes / patch.SnapshotCount are written;
// existing values in the row are preserved via COALESCE so a
// prune-only or check-only patch does not null out a backup-time
// size we already captured earlier the same day.
func (s *Store) UpsertHostRepoStatsHistory(
	ctx context.Context, hostID, day string, patch HostRepoStats, recordedAt time.Time,
) error {
	_, err := s.db.ExecContext(ctx, `
		INSERT INTO host_repo_stats_history
		  (host_id, day, total_size_bytes, snapshot_count, recorded_at)
		VALUES (?, ?, ?, ?, ?)
		ON CONFLICT(host_id, day) DO UPDATE SET
		  total_size_bytes = COALESCE(excluded.total_size_bytes, host_repo_stats_history.total_size_bytes),
		  snapshot_count   = COALESCE(excluded.snapshot_count,   host_repo_stats_history.snapshot_count),
		  recorded_at      = excluded.recorded_at`,
		hostID, day,
		nullableInt64(patch.TotalSizeBytes),
		nullableInt64(patch.SnapshotCount),
		recordedAt.UTC().Format(time.RFC3339Nano),
	)
	if err != nil {
		return fmt.Errorf("store: upsert host_repo_stats_history: %w", err)
	}
	return nil
}

// ListHostRepoStatsHistory returns all points for hostID with day
// >= since (UTC), ordered ascending. Pass time.Time{} to fetch the
// full history.
func (s *Store) ListHostRepoStatsHistory(
	ctx context.Context, hostID string, since time.Time,
) ([]RepoStatsHistoryPoint, error) {
	sinceStr := ""
	if !since.IsZero() {
		sinceStr = since.UTC().Format("2006-01-02")
	}
	rows, err := s.db.QueryContext(ctx, `
		SELECT day, total_size_bytes, snapshot_count
		FROM host_repo_stats_history
		WHERE host_id = ? AND day >= ?
		ORDER BY day ASC`,
		hostID, sinceStr)
	if err != nil {
		return nil, fmt.Errorf("store: list host_repo_stats_history: %w", err)
	}
	defer func() { _ = rows.Close() }()

	var out []RepoStatsHistoryPoint
	for rows.Next() {
		var (
			dayStr  string
			total   sql.NullInt64
			snapCnt sql.NullInt64
		)
		if err := rows.Scan(&dayStr, &total, &snapCnt); err != nil {
			return nil, fmt.Errorf("store: scan history row: %w", err)
		}
		d, perr := time.Parse("2006-01-02", dayStr)
		if perr != nil {
			return nil, fmt.Errorf("store: parse history day %q: %w", dayStr, perr)
		}
		p := RepoStatsHistoryPoint{Day: d}
		if total.Valid {
			v := total.Int64
			p.TotalSizeBytes = &v
		}
		if snapCnt.Valid {
			v := snapCnt.Int64
			p.SnapshotCount = &v
		}
		out = append(out, p)
	}
	if err := rows.Err(); err != nil && !errors.Is(err, sql.ErrNoRows) {
		return nil, fmt.Errorf("store: iterate history rows: %w", err)
	}
	return out, nil
}
