package api

import (
	"errors"
	"net/http"
	"strconv"

	"github.com/go-chi/chi/v5"

	"git.fabledsword.com/bvandeusen/minstrel/internal/apierror"
	"git.fabledsword.com/bvandeusen/minstrel/internal/tracks"
)

// removeTrackResponse is the wire shape from spec §5 (M7 #372). The three
// optional fields are omitted unless the corresponding cleanup actually
// happened, so the response stays minimal in the common case.
type removeTrackResponse struct {
	DeletedTrackID        string  `json:"deleted_track_id"`
	DeletedAlbumID        *string `json:"deleted_album_id,omitempty"`
	DeletedArtistID       *string `json:"deleted_artist_id,omitempty"`
	LidarrUnmonitorFailed *bool   `json:"lidarr_unmonitor_failed,omitempty"`
}

// handleRemoveTrack implements DELETE /api/admin/tracks/{id}?unmonitor=true|false.
//
// Admin-only (gated by auth.RequireAdmin on the /admin route group). Always
// deletes the file + DB row and runs the album/artist cascade tidy-up. When
// unmonitor=true and the track has an mbid, also calls Lidarr.UnmonitorTrack
// — failure there is non-fatal (the destructive part already completed) and
// surfaces as `lidarr_unmonitor_failed: true` in the success envelope.
//
// Per spec §5, Lidarr-side errors during the unmonitor step do NOT map to
// wire error codes; the only error codes this handler emits are not_found,
// server_error, plus the auth codes the middleware emits upstream.
func (h *handlers) handleRemoveTrack(w http.ResponseWriter, r *http.Request) {
	idStr := chi.URLParam(r, "id")
	trackID, ok := parseUUID(idStr)
	if !ok {
		// Malformed id is functionally equivalent to "no such track" for
		// the spec's error surface — collapse both to 404 not_found so
		// the client doesn't have to handle a separate bad_request branch
		// for a code path that's only reachable via UI bugs.
		writeErr(w, &apierror.Error{Status: http.StatusNotFound, Code: "not_found", Message: "track not found"})
		return
	}

	unmonitor := false
	if v := r.URL.Query().Get("unmonitor"); v != "" {
		if parsed, err := strconv.ParseBool(v); err == nil {
			unmonitor = parsed
		}
	}

	// Defensive: RequireUser+RequireAdmin should have run upstream. If
	// we got here without a user in context the routing is broken.
	admin, ok := requireUser(w, r)
	if !ok {
		return
	}

	deletedAlbum, deletedArtist, lidarrUnmonitorFailed, err := h.tracks.RemoveTrack(
		r.Context(), trackID, admin.ID, unmonitor,
	)
	if err != nil {
		if errors.Is(err, tracks.ErrNotFound) {
			writeErr(w, &apierror.Error{Status: http.StatusNotFound, Code: "not_found", Message: "track not found"})
			return
		}
		h.logger.Error("api: remove track failed", "err", err, "track_id", idStr)
		writeErr(w, apierror.InternalMsg("remove failed", err))
		return
	}

	resp := removeTrackResponse{DeletedTrackID: idStr}
	if deletedAlbum != nil {
		s := uuidToString(*deletedAlbum)
		resp.DeletedAlbumID = &s
	}
	if deletedArtist != nil {
		s := uuidToString(*deletedArtist)
		resp.DeletedArtistID = &s
	}
	// Only emit the flag when unmonitor was requested AND it failed.
	// Avoids "false" cluttering the wire when the operator didn't ask
	// for an unmonitor in the first place.
	if unmonitor && lidarrUnmonitorFailed {
		t := true
		resp.LidarrUnmonitorFailed = &t
	}

	writeJSON(w, http.StatusOK, resp)
}
