package db

import (
	"context"
	"errors"
	"fmt"
	"time"

	"github.com/jackc/pgx/v5"
)

// UpdateCheckRow is the persisted result of the last update-available
// check for an instance (WI-14). CHECK-ONLY bookkeeping.
type UpdateCheckRow struct {
	InstanceID       string
	AppID            string
	Branch           string
	InstalledBuildID string
	LatestBuildID    string
	UpdateAvailable  bool
	Error            string
	CheckedAt        time.Time
}

// UpsertUpdateCheck records the latest check result for an instance.
func (db *DB) UpsertUpdateCheck(ctx context.Context, r UpdateCheckRow) error {
	_, err := db.pool.Exec(ctx, `
		INSERT INTO update_checks (instance_id, app_id, branch, installed_buildid, latest_buildid, update_available, error, checked_at)
		VALUES ($1, $2, $3, $4, $5, $6, $7, NOW())
		ON CONFLICT (instance_id) DO UPDATE SET
			app_id = EXCLUDED.app_id,
			branch = EXCLUDED.branch,
			installed_buildid = EXCLUDED.installed_buildid,
			latest_buildid = EXCLUDED.latest_buildid,
			update_available = EXCLUDED.update_available,
			error = EXCLUDED.error,
			checked_at = NOW()
	`, r.InstanceID, r.AppID, r.Branch, r.InstalledBuildID, r.LatestBuildID, r.UpdateAvailable, r.Error)
	if err != nil {
		return fmt.Errorf("upsert update check: %w", err)
	}
	return nil
}

// GetUpdateCheck returns the last stored check for an instance, or
// (nil, nil) when the instance has never been checked.
func (db *DB) GetUpdateCheck(ctx context.Context, instanceID string) (*UpdateCheckRow, error) {
	row := db.pool.QueryRow(ctx, `
		SELECT instance_id, app_id, branch, installed_buildid, latest_buildid, update_available, error, checked_at
		FROM update_checks WHERE instance_id = $1
	`, instanceID)
	var r UpdateCheckRow
	err := row.Scan(&r.InstanceID, &r.AppID, &r.Branch, &r.InstalledBuildID, &r.LatestBuildID, &r.UpdateAvailable, &r.Error, &r.CheckedAt)
	if errors.Is(err, pgx.ErrNoRows) {
		return nil, nil
	}
	if err != nil {
		return nil, fmt.Errorf("get update check: %w", err)
	}
	return &r, nil
}

// ListUpdateChecks returns every stored check, keyed for the dashboard's
// "Update available" badges.
func (db *DB) ListUpdateChecks(ctx context.Context) ([]UpdateCheckRow, error) {
	rows, err := db.pool.Query(ctx, `
		SELECT instance_id, app_id, branch, installed_buildid, latest_buildid, update_available, error, checked_at
		FROM update_checks ORDER BY instance_id
	`)
	if err != nil {
		return nil, fmt.Errorf("list update checks: %w", err)
	}
	defer rows.Close()
	var out []UpdateCheckRow
	for rows.Next() {
		var r UpdateCheckRow
		if err := rows.Scan(&r.InstanceID, &r.AppID, &r.Branch, &r.InstalledBuildID, &r.LatestBuildID, &r.UpdateAvailable, &r.Error, &r.CheckedAt); err != nil {
			return nil, fmt.Errorf("scan update check: %w", err)
		}
		out = append(out, r)
	}
	return out, rows.Err()
}

// DeleteUpdateCheck removes an instance's row (instance deleted).
func (db *DB) DeleteUpdateCheck(ctx context.Context, instanceID string) error {
	_, err := db.pool.Exec(ctx, `DELETE FROM update_checks WHERE instance_id = $1`, instanceID)
	if err != nil {
		return fmt.Errorf("delete update check: %w", err)
	}
	return nil
}
