package pocketbase

import (
	"strings"
	"testing"

	"github.com/pocketbase/pocketbase/core"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"git.vakhrushev.me/av/transcriber/internal/adapter/repo/pocketbase/migrations"
)

// newTestStorage поднимает хранилище на пустом каталоге и накатывает схему —
// тем же путём, каким это делает сервис при старте.
func newTestStorage(t *testing.T) core.App {
	t.Helper()

	app, err := New(t.TempDir())
	require.NoError(t, err)
	t.Cleanup(func() {
		if err := app.ResetBootstrapState(); err != nil {
			t.Logf("не удалось закрыть хранилище: %v", err)
		}
	})

	return app
}

// Критерий приёмки 10. Содержимое записи закрыто во всех коллекциях, куда оно
// переехало.
//
// Прежде содержимое лежало одной колонкой задачи, и закрывала его одна норма про
// файл записи. Теперь оно живёт в шести коллекциях, и реализация, следующая
// только прежней норме, завела бы поле вложения с умолчанием библиотеки: ссылка
// на сырой ответ провайдера — а это полный текст речи — отдавала бы его любому,
// кто её знает, без сессии.
func TestRecordContentIsClosedEverywhere(t *testing.T) {
	app := newTestStorage(t)

	// Правило просмотра остаётся незаданным, то есть «только владелец панели».
	// Содержимое отдаёт собственный адрес сервиса, а не поверхность хранилища;
	// непустое правило открыло бы перечисление коллекции впрок.
	for _, name := range []string{
		migrations.RecordsCollection,
		migrations.TextsCollection,
		migrations.StructuresCollection,
		migrations.RecognitionsCollection,
		migrations.RecordEventsCollection,
		migrations.TopicsCollection,
	} {
		collection, err := app.FindCollectionByNameOrId(name)
		require.NoError(t, err, "коллекция %s заведена шагом схемы", name)

		assert.Nil(t, collection.ListRule, "перечисление %s закрыто", name)
		assert.Nil(t, collection.ViewRule, "чтение %s закрыто", name)
		assert.Nil(t, collection.CreateRule, "заведение записи в %s закрыто", name)
		assert.Nil(t, collection.UpdateRule, "правка %s закрыта", name)
		assert.Nil(t, collection.DeleteRule, "удаление из %s закрыто", name)
	}

	// А поле вложения помечено защищённым: без пометки ссылка открывает
	// содержимое любому, кто её знает, и знание ссылки становится правом.
	recognitions, err := app.FindCollectionByNameOrId(migrations.RecognitionsCollection)
	require.NoError(t, err)

	field := recognitions.Fields.GetByName("payload")
	require.NotNil(t, field, "поле сохранённого ответа заведено")

	file, ok := field.(*core.FileField)
	require.True(t, ok, "сохранённый ответ лежит вложением, а не колонкой")
	assert.True(t, file.Protected, "поле вложения защищено")
}

// Прежняя коллекция задач уходит вместе с моделью: данных под ней не было, а
// пустая копия висела бы в панели вторым домом для понятия, которого больше нет.
func TestFormerJobsCollectionIsGone(t *testing.T) {
	app := newTestStorage(t)

	_, err := app.FindCollectionByNameOrId(migrations.JobsCollection)
	assert.Error(t, err, "прежней коллекции задач не осталось")
}

// Пара «запись и вид» уникальна: повтор прерванного шага не заводит второго
// комплекта строк, и вопрос «какой текст отдавать человеку» не становится
// вопросом порядка записи.
func TestAppendicesAreUniquePerRecord(t *testing.T) {
	app := newTestStorage(t)

	indexes := map[string][]string{
		migrations.TextsCollection:      {"idx_texts_record_kind"},
		migrations.StructuresCollection: {"idx_structures_record_version"},
		migrations.TopicsCollection:     {"idx_topics_owner_name"},
	}

	for name, expected := range indexes {
		collection, err := app.FindCollectionByNameOrId(name)
		require.NoError(t, err)

		for _, index := range expected {
			var found bool
			for _, declared := range collection.Indexes {
				if strings.Contains(declared, index) && strings.Contains(declared, "UNIQUE") {
					found = true
				}
			}
			assert.Truef(t, found, "у %s есть уникальный индекс %s", name, index)
		}
	}
}
