package http

import (
	"log/slog"
	"net/http"
	"net/netip"

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

// AppChain одевает адреса приложения в их слои.
//
// Порядок один и он несущий: ограничитель частоты → подстановка заголовков →
// узнавание → требование учётной записи → обработчик.
//
//   - ограничитель стоит первым, потому что узнавание читает базу, а на новом
//     имени ещё и пишет в неё: поставленное раньше, оно работало бы на запросах,
//     которые ограничитель уже отверг, и поток отвергнутых обращений заводил бы
//     учётные записи, которые потом не убираются ничем;
//   - подстановка стоит перед узнаванием и после ограничителя: она правит
//     заголовки, а ограничитель считает по адресу спрашивающего, и заголовки
//     входа его не касаются;
//   - требование учётной записи стоит перед чтением тела: запись, за которую не
//     заплатит узнанный отправитель, не должна попасть даже в память, а позже
//     пришлось бы убирать уже уложенный файл — чего сервис не умеет вовсе.
//
// Подстановка — звено необязательное: пустой перечень значит, что отладочный
// запуск выключен, и слой отдаёт цепочку нетронутой — следующее звено как есть.
// Цепочка при этом ровно та же, что была до появления предохранителя, — лишнего
// звена в бою нет. Судит эту пустоту сам слой, и предикат «подставляем ли» имеет
// поэтому одно представление — непустоту перечня; здесь не решается ничего.
//
// Область действия всех — корень приложения, и берётся она из перечня
// адресного пространства: цепочка вешается на корень целиком, вторым списком
// адресов её не описывают.
func AppChain(
	routes http.Handler,
	users contract.UserRepository,
	trusted []netip.Prefix,
	substitution map[string]string,
	logger *slog.Logger,
) http.Handler {
	handler := RequireUser()(routes)
	handler = TrustedHeaderIdentity(users, trusted, logger)(handler)
	handler = SubstituteIdentityHeaders(substitution, trusted, logger)(handler)
	handler = RateLimit(trusted)(handler)
	return handler
}

// BuildHandler собирает поверхность сервиса целиком.
//
// Раздача приложения вешается последней: она занимает корень, и всё, что не
// совпало ни с одним адресом сервиса, доходит до неё.
//
// Снаружи всех — журнал и восстановление после паники: первый обязан видеть код
// ответа, каким бы слоем тот ни родился, второй — не дать одному паникующему
// запросу уронить процесс.
func BuildHandler(mounts []Mount, webapp *WebappHandler, logger *slog.Logger) http.Handler {
	mux := http.NewServeMux()

	RegisterServiceRoutes(mux, mounts)
	webapp.Register(mux)

	return Journal(mounts, logger)(Recover(logger)(mux))
}
