package api

import (
	"context"
	"errors"
	"net/http"
	"strings"

	"git.fabledsword.com/bvandeusen/minstrel/internal/apierror"
	"git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq"
	"git.fabledsword.com/bvandeusen/minstrel/internal/lidarr"
)

// lidarrSearchResult is the JSON shape returned by GET /api/lidarr/search.
// Field names follow the lowercase snake_case convention used by all /api/*
// responses. artist_mbid and album_mbid are the parent identifiers surfaced
// by LookupAlbum / LookupTrack; they are empty strings on artist results.
type lidarrSearchResult struct {
	MBID          string `json:"mbid"`
	Name          string `json:"name"`
	SecondaryText string `json:"secondary_text"`
	ImageURL      string `json:"image_url"`
	ArtistMBID    string `json:"artist_mbid"`
	AlbumMBID     string `json:"album_mbid"`
	InLibrary     bool   `json:"in_library"`
	Requested     bool   `json:"requested"`
}

// handleLidarrSearch implements GET /api/lidarr/search?q=&kind=artist|album|track.
//
// Steps:
//  1. Validate kind and q params.
//  2. Load lidarr config; 503 when disabled.
//  3. Call the matching Lidarr lookup.
//  4. For each result, compute in_library (EXISTS by MBID) and requested
//     (HasNonTerminalRequestForMBID).
//  5. Return normalized JSON array.
func (h *handlers) handleLidarrSearch(w http.ResponseWriter, r *http.Request) {
	kind := strings.TrimSpace(r.URL.Query().Get("kind"))
	switch kind {
	case "artist", "album", "track":
		// valid
	default:
		writeErr(w, apierror.BadRequest("bad_kind", "kind must be artist, album, or track"))
		return
	}

	q := strings.TrimSpace(r.URL.Query().Get("q"))
	if q == "" {
		writeErr(w, apierror.BadRequest("missing_query", "q is required"))
		return
	}

	cfg, err := h.lidarrCfg.Get(r.Context())
	if err != nil {
		h.logger.Error("api: lidarr search: load config", "err", err)
		writeErr(w, &apierror.Error{Status: 500, Code: "lidarr_lookup_failed", Message: "failed to load lidarr config", Cause: err})
		return
	}
	if !cfg.Enabled {
		writeErr(w, &apierror.Error{Status: 503, Code: "lidarr_disabled", Message: "Lidarr integration is not enabled"})
		return
	}

	client := lidarr.NewClient(cfg.BaseURL, cfg.APIKey)

	var results []lidarr.LookupResult
	switch kind {
	case "artist":
		results, err = client.LookupArtist(r.Context(), q)
	case "album":
		results, err = client.LookupAlbum(r.Context(), q)
	case "track":
		results, err = client.LookupTrack(r.Context(), q)
	}
	if err != nil {
		switch {
		case errors.Is(err, lidarr.ErrUnreachable):
			writeErr(w, &apierror.Error{Status: 503, Code: "lidarr_unreachable", Message: "Lidarr is unreachable", Cause: err})
		case errors.Is(err, lidarr.ErrAuthFailed):
			writeErr(w, &apierror.Error{Status: 503, Code: "lidarr_auth_failed", Message: "Lidarr authentication failed", Cause: err})
		default:
			h.logger.Error("api: lidarr search: lookup failed", "kind", kind, "err", err)
			writeErr(w, &apierror.Error{Status: 500, Code: "lidarr_lookup_failed", Message: "Lidarr lookup failed", Cause: err})
		}
		return
	}

	dbQ := dbq.New(h.pool)
	out := make([]lidarrSearchResult, 0, len(results))
	for _, res := range results {
		inLib, libErr := h.lidarrInLibrary(r.Context(), kind, res.MBID)
		if libErr != nil {
			h.logger.Error("api: lidarr search: in_library check failed", "err", libErr)
			writeErr(w, &apierror.Error{Status: 500, Code: "lidarr_lookup_failed", Message: "library check failed", Cause: libErr})
			return
		}
		requested, reqErr := dbQ.HasNonTerminalRequestForMBID(r.Context(), res.MBID)
		if reqErr != nil {
			h.logger.Error("api: lidarr search: requested check failed", "err", reqErr)
			writeErr(w, &apierror.Error{Status: 500, Code: "lidarr_lookup_failed", Message: "request check failed", Cause: reqErr})
			return
		}
		out = append(out, lidarrSearchResult{
			MBID:          res.MBID,
			Name:          res.Name,
			SecondaryText: res.Secondary,
			ImageURL:      res.ImageURL,
			ArtistMBID:    res.ArtistMBID,
			AlbumMBID:     res.AlbumMBID,
			InLibrary:     inLib,
			Requested:     requested,
		})
	}
	writeJSON(w, http.StatusOK, out)
}

// lidarrInLibrary checks whether the given MBID exists in the corresponding
// local library table based on kind. Runs a direct EXISTS query since
// these per-kind MBID lookups are not in the sqlc-generated query set.
func (h *handlers) lidarrInLibrary(ctx context.Context, kind, mbid string) (bool, error) {
	var query string
	switch kind {
	case "artist":
		query = `SELECT EXISTS(SELECT 1 FROM artists WHERE mbid = $1)`
	case "album":
		query = `SELECT EXISTS(SELECT 1 FROM albums WHERE mbid = $1)`
	case "track":
		query = `SELECT EXISTS(SELECT 1 FROM tracks WHERE mbid = $1)`
	default:
		return false, nil
	}
	var exists bool
	if err := h.pool.QueryRow(ctx, query, mbid).Scan(&exists); err != nil {
		return false, err
	}
	return exists, nil
}
