package pocketbase

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

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

// BindPanelRules подчиняет правку записи в панели тем же правилам, что и правку
// из кода.
//
// Панель — вход в запись наравне с конвейером, а не окно просмотра: ради правки
// она и покупалась, остановленная запись возвращается в работу снятием признака.
// Но правка полем идёт мимо кода, который чистит служебные поля, и владелец,
// «вернувший запись в работу», получил бы запись с прежним признаком захвата
// (захвату она не выдастся до конца срока), с числом отказов на пределе
// (остановится от первого же отказа) и со старым временем входа в рубеж
// (остановится снова первым же захватом по пределу простоя). Узнать об этом ему
// неоткуда.
//
// Правило живёт **одним местом** — доменными `Resume` и `MoveToState`, — и хук
// зовёт именно их, а не повторяет перечень служебных полей колонками. Повтор
// перечня был бы вторым домом того же правила: новый сторож попал бы в домен и
// не попал в панель, и владелец «вернул бы запись в работу», а она снова выпала
// бы из выборки — молча.
//
// Хук стоит на правке **запросом**, а не на всяком сохранении записи. Модельное
// событие не различает, кто пишет, и срабатывало бы на каждом переходе
// конвейера: тогда пауза, поставленная шагом вместе со сменой рубежа, стиралась
// бы тем же сохранением, а число отказов остановленной записи — которое
// остановка хранит намеренно — приходило бы владельцу нулём.
func BindPanelRules(app core.App) {
	app.OnRecordUpdateRequest(migrations.RecordsCollection).BindFunc(func(e *core.RecordRequestEvent) error {
		original := e.Record.Original()
		if original == nil {
			return e.Next()
		}

		stateChanged := original.GetString("state") != e.Record.GetString("state")
		// Снятие признака остановки — то самое движение, ради которого признак и
		// заведён: запись возвращается в работу с сохранённого рубежа.
		resumed := !original.GetDateTime("halted_at").IsZero() &&
			e.Record.GetDateTime("halted_at").IsZero()

		if !stateChanged && !resumed {
			return e.Next()
		}

		// Запись читается уже с правкой человека: рубеж здесь тот, который он
		// выбрал, а признак остановки — тот, который он снял или оставил.
		record := recordToAudioRecord(e.Record)
		switch {
		case resumed:
			record.Resume()
		default:
			record.MoveToState(record.State)
		}
		applyOwnedByPipeline(e.Record, record)

		if err := e.Next(); err != nil {
			return err
		}

		if resumed {
			// Перезапуск виден в журнале событий с указанием, что его сделал
			// человек: иначе запись, вернувшаяся в работу, выглядела бы как
			// запись, которая туда и не уходила.
			//
			// Строка пишется **после** сохранения: событие о правке, которая не
			// прошла, соврало бы о состоянии записи. Отказ записи журнала саму
			// правку не отменяет — журнал никем не читается ради решения.
			event := &entity.RecordEvent{
				RecordID: e.Record.Id,
				Origin:   entity.EventOriginHuman,
				Step:     "resume",
				Outcome:  entity.EventOutcomeResumed,
			}
			if err := NewRecordEventRepository(e.App).Append(event); err != nil {
				e.App.Logger().Error("Failed to log record resume", "error", err, "record_id", e.Record.Id)
			}
		}

		return nil
	})
}
