// Package subsonic implements a Subsonic-compatible REST API surface under
// /rest. It exists for third-party Subsonic clients (Symfonium, Substreamer,
// DSub, Tempo, play:Sub, …). Minstrel's own clients use the native /api/*
// surface, which has modern auth; /rest/* retains Subsonic's legacy auth for
// compatibility only. See docs/auth.md for the security posture.
package subsonic

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

	"github.com/go-chi/chi/v5"
	"github.com/jackc/pgx/v5/pgxpool"

	"git.fabledsword.com/bvandeusen/minstrel/internal/playevents"
)

// Mount attaches Subsonic handlers at /rest on r. Endpoints are exposed at
// both /rest/foo and /rest/foo.view because client conventions vary. Both
// GET and POST are accepted; Subsonic's auth params live in the query string
// either way.
func Mount(r chi.Router, pool *pgxpool.Pool, logger *slog.Logger, cfg Config, events *playevents.Writer) {
	b := &browseHandlers{pool: pool}
	m := newMediaHandlers(pool, events, logger)
	r.Route("/rest", func(sub chi.Router) {
		sub.Use(Middleware(pool, cfg))
		register(sub, "/ping", handlePing)
		register(sub, "/getLicense", handleGetLicense)
		register(sub, "/getMusicFolders", b.getMusicFolders)
		register(sub, "/getIndexes", b.getIndexes)
		register(sub, "/getArtists", b.getArtists)
		register(sub, "/getArtist", b.getArtist)
		register(sub, "/getAlbum", b.getAlbum)
		register(sub, "/getAlbumList2", b.getAlbumList2)
		register(sub, "/getSong", b.getSong)
		register(sub, "/search3", b.search3)
		register(sub, "/stream", m.handleStream)
		register(sub, "/download", m.handleDownload)
		register(sub, "/getCoverArt", m.handleGetCoverArt)
		register(sub, "/scrobble", m.handleScrobble)
		register(sub, "/star", m.handleStar)
		register(sub, "/unstar", m.handleUnstar)
		register(sub, "/getStarred", m.handleGetStarred)
		register(sub, "/getStarred2", m.handleGetStarred2)
		register(sub, "/getUser", handleGetUser)

		// Subsonic clients expect every /rest/* miss to come back as a
		// Subsonic-shaped error envelope, not chi's plain-text 404. Without
		// this, hitting an unimplemented method (Feishin's first-login probe
		// hits getUser, getPlaylists, etc.) breaks the client's parser and
		// surfaces as a generic "failed to log in".
		sub.NotFound(func(w http.ResponseWriter, r *http.Request) {
			WriteFail(w, r, ErrGeneric, "Method not implemented")
		})
	})
}

func register(r chi.Router, path string, h func(http.ResponseWriter, *http.Request)) {
	r.Get(path, h)
	r.Post(path, h)
	r.Get(path+".view", h)
	r.Post(path+".view", h)
}
