package migrations

import (
	"github.com/pocketbase/pocketbase/core"

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

func up202608110001(app core.App) error {
	files := core.NewBaseCollection(FilesCollection)
	files.Fields.Add(
		// Сам файл. Защищённым поле не помечено намеренно: право прочитать
		// запись даёт знание её идентификатора, и файл встаёт вровень с опросом
		// готовности задачи, а не ниже.
		//
		// Потолок задан **числом**: нулём библиотека читает не «без предела», а
		// своё умолчание в 5 МиБ, и на нём отваливалось бы всё длиннее пяти
		// минут. Число выведено из расчётного потолка записи в шесть часов с
		// запасом на видео; оно же стоит строкой в docs/database.md.
		&core.FileField{Name: "file", MaxSelect: 1, MaxSize: entity.MaxRecordSize},
		// Где лежит копия. Поле названо `location`, а не `storage`: последним
		// словом зовут само хранилище, и третий смысл развёл бы одно слово по
		// разным вещам.
		&core.SelectField{
			Name:      "location",
			Values:    []string{entity.LocationLocal, entity.LocationS3},
			MaxSelect: 1,
			Required:  true,
		},
		// Ключ объекта во внешнем хранилище; у местной копии пуст.
		&core.TextField{Name: "object_key"},
		&core.NumberField{Name: "size", OnlyInt: true},
		&core.AutodateField{Name: "created", OnCreate: true},
		&core.AutodateField{Name: "updated", OnCreate: true, OnUpdate: true},
	)

	if err := app.Save(files); err != nil {
		return err
	}

	jobs := core.NewBaseCollection(JobsCollection)
	jobs.Fields.Add(
		// Перечень состояний закрыт схемой: задача, заведённая в панели руками,
		// не должна попасть в выборку с состоянием, которого конвейер не знает.
		&core.SelectField{
			Name: "state",
			Values: []string{
				entity.StateCreated,
				entity.StateConverted,
				entity.StateTranscribe,
				entity.StateDone,
				entity.StateFailed,
				entity.StateDead,
			},
			MaxSelect: 1,
			Required:  true,
		},
		&core.SelectField{
			Name:      "source",
			Values:    []string{entity.SourceUnknown, entity.SourceApi, entity.SourceTelegram},
			MaxSelect: 1,
			Required:  true,
		},
		// Текущий файл задачи: шаг конвейера переставляет ссылку на свой
		// результат.
		// Обязательна: задача без записи не может пройти ни одного шага, и
		// заведённая в панели руками она дошла бы до шага только затем, чтобы
		// отказать. Компилятор этого не держит — держит схема.
		&core.RelationField{
			Name:         "file",
			CollectionId: files.Id,
			MaxSelect:    1,
			Required:     true,
		},
		&core.TextField{Name: "error_text"},
		&core.TextField{Name: "acquisition_id"},
		&core.DateField{Name: "acquire_time"},
		&core.DateField{Name: "delay_time"},
		// Число попыток: растёт при каждом захвате, обнуляется на шаге,
		// завершившемся без отказа.
		&core.NumberField{Name: "attempts", OnlyInt: true, Min: ptr(0.0)},
		&core.TextField{Name: "recognition_op_id"},
		&core.EditorField{Name: "transcription_text"},
		&core.NumberField{Name: "tg_chat_id", OnlyInt: true},
		&core.NumberField{Name: "tg_reply_message_id", OnlyInt: true},
		&core.AutodateField{Name: "created", OnCreate: true},
		&core.AutodateField{Name: "updated", OnCreate: true, OnUpdate: true},
	)

	// Выборка воркера идёт по состоянию, паузе и сроку захвата — индекс по
	// состоянию снимает полный перебор, который был у прежней таблицы.
	jobs.AddIndex("idx_transcribe_jobs_state", false, "state", "")

	return app.Save(jobs)
}

func down202608110001(app core.App) error {
	// Порядок обратный порядку заведения: задачи ссылаются на файлы.
	for _, name := range []string{JobsCollection, FilesCollection} {
		collection, err := app.FindCollectionByNameOrId(name)
		if err != nil {
			continue
		}
		if err := app.Delete(collection); err != nil {
			return err
		}
	}
	return nil
}
