package db

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

// ErrUploadNotFound indicates the requested upload UUID does not exist.
var ErrUploadNotFound = errors.New("db: upload not found")

// UploadRow represents a row in the uploads table.
type UploadRow struct {
	ID           int64
	UUID         string
	RepositoryID int64
	ByteOffset   int64
}

// CreateUpload inserts a new upload row and returns its ID.
func (d *DB) CreateUpload(uuid string, repoID int64) error {
	_, err := d.Exec(
		`INSERT INTO uploads (uuid, repository_id) VALUES (?, ?)`,
		uuid, repoID,
	)
	if err != nil {
		return fmt.Errorf("db: create upload: %w", err)
	}
	return nil
}

// GetUpload returns the upload with the given UUID.
func (d *DB) GetUpload(uuid string) (*UploadRow, error) {
	var u UploadRow
	err := d.QueryRow(
		`SELECT id, uuid, repository_id, byte_offset FROM uploads WHERE uuid = ?`, uuid,
	).Scan(&u.ID, &u.UUID, &u.RepositoryID, &u.ByteOffset)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return nil, ErrUploadNotFound
		}
		return nil, fmt.Errorf("db: get upload: %w", err)
	}
	return &u, nil
}

// UpdateUploadOffset sets the byte_offset for an upload.
func (d *DB) UpdateUploadOffset(uuid string, offset int64) error {
	result, err := d.Exec(
		`UPDATE uploads SET byte_offset = ? WHERE uuid = ?`,
		offset, uuid,
	)
	if err != nil {
		return fmt.Errorf("db: update upload offset: %w", err)
	}
	n, err := result.RowsAffected()
	if err != nil {
		return fmt.Errorf("db: update upload offset rows affected: %w", err)
	}
	if n == 0 {
		return ErrUploadNotFound
	}
	return nil
}

// DeleteUpload removes the upload row with the given UUID.
func (d *DB) DeleteUpload(uuid string) error {
	result, err := d.Exec(`DELETE FROM uploads WHERE uuid = ?`, uuid)
	if err != nil {
		return fmt.Errorf("db: delete upload: %w", err)
	}
	n, err := result.RowsAffected()
	if err != nil {
		return fmt.Errorf("db: delete upload rows affected: %w", err)
	}
	if n == 0 {
		return ErrUploadNotFound
	}
	return nil
}
