package sqlite

import (
	"bytes"
	"context"
	"database/sql"
	"fmt"
	"io"

	"git.vakhrushev.me/av/transcriber/internal/clock"
	"git.vakhrushev.me/av/transcriber/internal/entity"
	"git.vakhrushev.me/av/transcriber/internal/ident"
)

// nullString — обёртка ради читаемости выборок: колонка, допускающая пустое
// значение, читается в неё, а домену отдаётся указателем.
type nullString struct {
	sql.NullString
}

// bytesReader отдаёт содержимое в памяти потоком: сохранённый ответ провайдера
// приходит целиком байтами, а укладка принимает поток.
func bytesReader(raw []byte) io.Reader {
	return bytes.NewReader(raw)
}

type RecordEventRepository struct {
	db *DB
}

func NewRecordEventRepository(db *DB) *RecordEventRepository {
	return &RecordEventRepository{db: db}
}

// Append пишет строку журнала событий записи.
//
// Журнал пишется на смену рубежа, на остановку и на возврат в работу, а не на
// каждое откладывание опроса: часовая запись дала бы сотни строк ни о чём. Ни
// один шаг конвейера его не читает, чтобы решить, что делать дальше: решение
// принимается по рубежу записи, и второй источник решения разошёлся бы с первым
// молча.
//
// Содержимое записи сюда не попадает — инвариант приватности действует здесь
// наравне с журналом сервиса.
func (repo *RecordEventRepository) Append(event *entity.RecordEvent) error {
	if event.Id == "" {
		event.Id = ident.New()
	}

	_, err := repo.db.Writer().ExecContext(context.Background(),
		`INSERT INTO record_events
		    (id, record_id, origin, step, outcome, outcome_text, duration_ms, created_at)
		 VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
		event.Id, event.RecordID, event.Origin, event.Step,
		event.Outcome, event.OutcomeText, event.DurationMs, formatTime(clock.Now()),
	)
	if err != nil {
		return fmt.Errorf("failed to append event of record %s: %w", event.RecordID, err)
	}

	return nil
}
