package contract

import (
	"errors"
	"fmt"
)

// ErrOwnerRequired — приём по HTTP дошёл до заведения задачи, а владельца ему не
// назвали. Значение сентинельное: нести отказу нечего, а имя учётной записи в
// него не кладётся никогда.
//
// Достижимого случая у него нет: узнавание заводит учётную запись само, и
// предъявителя без неё под корнем приложения не бывает. Отдельной ветви ответа
// он поэтому не получает — ветвь по умолчанию читает его как аварию сервиса,
// каковой он и был бы.
var ErrOwnerRequired = errors.New("owner is required to accept a record")

// ErrRecordUnreadable — присланную запись не удалось прочитать: источник
// метаданных не разобрал её содержимое. Причина отказа — сама запись, а не сбой
// сервиса, и код ответа обязан называть причину, а не место.
//
// Заводится sentinel'ом, а не остаётся голой ошибкой источника метаданных:
// ветвь по умолчанию отдала бы `500`, и «файл негоден» читалось бы как «сломался
// сервер». Своих данных отказу нести нечего — имя файла в него не кладётся
// никогда.
var ErrRecordUnreadable = errors.New("uploaded record cannot be read")

// ErrRecordTooLarge — присланная запись длиннее потолка размера. Самый частый
// отказ у человека на мобильной сети, и прежде он уходил телом ограничителя тела
// — мимо единой формы отказа.
var ErrRecordTooLarge = errors.New("uploaded record exceeds size limit")

// ErrTextNotReady — текста запрошенного вида у записи ещё нет. Состояние, а не
// отсутствие: запись есть и принадлежит спрашивающему, просто конвейер до этого
// вида не дошёл. Отвечать на это тем же, чем отвечает чужая запись, нельзя —
// человек увидел бы «не найдено» на своей записи, загруженной минуту назад.
var ErrTextNotReady = errors.New("requested text view is not ready yet")

// ErrCopyNotReady — копии записи запрошенного вида у неё ещё нет. Состояние, а
// не отсутствие, и код у него тот же, что у ненаписанного текста: пустой ответ
// читался бы как пустой файл, а «не найдено» слилось бы с ответом на чужую и
// неизвестную запись — человек увидел бы его на своей записи, загруженной
// минуту назад.
var ErrCopyNotReady = errors.New("requested file copy is not ready yet")

// ErrTooManyRequests — бюджет ограничителя частоты выбран. Признак заводится
// затем, чтобы отказ ограничителя уходил той же формой тела, что и отказ
// обработчика: он рождается слоем и до обработчика не доходит вовсе.
var ErrTooManyRequests = errors.New("request rate budget is exhausted")

// ErrBadRequest — во входе запроса негодное значение: неизвестный вид текста,
// нечитаемый ключ страницы, отрицательный размер. Отличается от ErrRecordUnreadable
// тем, что негодна **просьба**, а не присланная запись.
var ErrBadRequest = errors.New("request input is not valid")

// ErrUnauthorized — сессии нет вовсе. Первая строка таблицы отображения, и без
// собственного признака она собиралась бы руками мимо единой точки: правка формы
// тела не доехала бы до неё, и два места разошлись бы молча.
var ErrUnauthorized = errors.New("session is required")

// ErrNotFound — под корнем приложения такого адреса нет. Отличается от
// JobNotFoundError тем, что не найдена **просьба**, а не запись: тело у ответа
// то же, но повод другой, и смешивать их в одном признаке значило бы называть
// отсутствующий адрес отсутствующей записью.
var ErrNotFound = errors.New("address not found")

// ErrLoginNotAcceptable — логин негоден: пустой, из одних пробельных знаков,
// длиннее предела или с управляющими знаками. Это не отказ хранилища, а негодный
// ввод, и звать по нему учётную запись не надо.
var ErrLoginNotAcceptable = errors.New("provider login is not acceptable")

type JobNotFoundError struct {
	State   string
	Message string
}

func (e *JobNotFoundError) Error() string {
	return fmt.Sprintf("%s - %s", e.State, e.Message)
}

// LostAcquisitionError — захват задачи за время работы шага достался другому.
// Шаг, получивший его, завершается без записи результата: иначе два воркера
// пишут в одну запись по очереди, портя её результат, и счётчик отказов
// сбрасывает тот, кто уже не владелец.
type LostAcquisitionError struct {
	JobID string
}

func (e *LostAcquisitionError) Error() string {
	return fmt.Sprintf("%s: job acquisition lost", e.JobID)
}

type NoopJobError struct {
	State string
}

func (e *NoopJobError) Error() string {
	return fmt.Sprintf("%s: no op job occur", e.State)
}
