// Package soundcloud implements a playlist.Provider backed by yt-dlp.
//
// SoundCloud is opt-in: it only registers when [soundcloud] enabled = true
// is set in config. Once enabled, search uses yt-dlp's "scsearch:" protocol
// and works without further configuration. When [soundcloud] user is set,
// browse exposes that profile's Tracks, Likes, and Reposts — public for most
// accounts. With cookies_from set, yt-dlp picks up the user's browser session
// for subscriber-gated content.
package soundcloud

import (
	"context"
	"fmt"
	"net/url"
	"slices"
	"strings"

	"github.com/bjarneo/cliamp/playlist"
	"github.com/bjarneo/cliamp/provider"
	"github.com/bjarneo/cliamp/resolve"
)

// Compile-time interface checks.
var (
	_ playlist.Provider = (*Provider)(nil)
	_ provider.Searcher = (*Provider)(nil)
)

// Config holds settings for the SoundCloud provider.
type Config struct {
	Enabled     bool   // true only when user explicitly sets enabled = true
	User        string // SoundCloud username (the path segment, e.g. "yourname"). Optional.
	CookiesFrom string // browser name for yt-dlp --cookies-from-browser (e.g. "firefox"). Optional.
}

// IsSet reports whether the SoundCloud provider should be exposed.
// SoundCloud is opt-in: requires enabled = true in [soundcloud].
func (c Config) IsSet() bool { return c.Enabled }

// Provider implements playlist.Provider and provider.Searcher for SoundCloud
// via yt-dlp.
type Provider struct {
	user string // optional configured username for browse
}

// defaultBrowse seeds the empty-user discovery view. SoundCloud's official
// charts/discover endpoints all 404 through yt-dlp, so these are search-backed
// virtual playlists — the ID is the yt-dlp page URL passed straight to
// ResolveYTDLBatch.
var defaultBrowse = []playlist.PlaylistInfo{
	{ID: "scsearch50:trending", Name: "Trending", Section: "Browse"},
	{ID: "scsearch50:hip hop", Name: "Hip-Hop", Section: "Browse"},
	{ID: "scsearch50:electronic", Name: "Electronic", Section: "Browse"},
	{ID: "scsearch50:house", Name: "House", Section: "Browse"},
	{ID: "scsearch50:lo-fi", Name: "Lo-Fi", Section: "Browse"},
	{ID: "scsearch50:indie", Name: "Indie", Section: "Browse"},
	{ID: "scsearch50:pop", Name: "Pop", Section: "Browse"},
}

// NewFromConfig returns a provider, or nil when SoundCloud is not enabled.
// Sets resolve's yt-dlp cookies as a side effect when CookiesFrom is non-empty
// so any yt-dlp invocation (search, browse, playback) uses the user's
// signed-in session.
func NewFromConfig(cfg Config) *Provider {
	if !cfg.Enabled {
		return nil
	}
	if cfg.CookiesFrom != "" {
		resolve.SetYTDLCookiesFrom(cfg.CookiesFrom)
	}
	return &Provider{user: strings.TrimSpace(cfg.User)}
}

func (p *Provider) Name() string { return "SoundCloud" }

// Playlists exposes the configured user's Tracks, Likes, and Reposts when a
// username is set; otherwise a curated set of genre searches so the empty
// state has something playable. Ctrl+F always opens search regardless.
func (p *Provider) Playlists() ([]playlist.PlaylistInfo, error) {
	if p.user == "" {
		return slices.Clone(defaultBrowse), nil
	}
	base := "https://soundcloud.com/" + url.PathEscape(p.user)
	return []playlist.PlaylistInfo{
		{ID: base + "/tracks", Name: "Tracks", Section: p.user},
		{ID: base + "/likes", Name: "Likes", Section: p.user},
		{ID: base + "/reposts", Name: "Reposts", Section: p.user},
	}, nil
}

// Tracks resolves a SoundCloud page URL (or scsearch query) via yt-dlp. The
// playlistID is the value produced by Playlists.
func (p *Provider) Tracks(playlistID string) ([]playlist.Track, error) {
	if playlistID == "" {
		return nil, fmt.Errorf("soundcloud: empty playlist id")
	}
	return resolve.ResolveYTDLBatch(playlistID, 0, 0)
}

// SearchTracks runs `yt-dlp scsearch{limit}:{query}` and returns matched
// tracks. Implements provider.Searcher.
func (p *Provider) SearchTracks(_ context.Context, query string, limit int) ([]playlist.Track, error) {
	q := strings.TrimSpace(query)
	if q == "" {
		return nil, nil
	}
	if limit <= 0 {
		limit = 10
	}
	return resolve.ResolveYTDLBatch(fmt.Sprintf("scsearch%d:%s", limit, q), 0, 0)
}
