package lastfm

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
)

const (
	apiBaseUrl = "https://ws.audioscrobbler.com/2.0/"
)

type httpDoer interface {
	Do(req *http.Request) (*http.Response, error)
}

func NewClient(apiKey string, lang string, hc httpDoer) *Client {
	return &Client{apiKey, lang, hc}
}

type Client struct {
	apiKey string
	lang   string
	hc     httpDoer
}

func (c *Client) makeRequest(params url.Values) (*Response, error) {
	params.Add("format", "json")
	params.Add("api_key", c.apiKey)

	req, _ := http.NewRequest("GET", apiBaseUrl, nil)
	req.URL.RawQuery = params.Encode()

	resp, err := c.hc.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode != 200 {
		return nil, c.parseError(data)
	}

	var response Response
	err = json.Unmarshal(data, &response)

	return &response, err
}

func (c *Client) ArtistGetInfo(ctx context.Context, name string, mbid string) (*Artist, error) {
	params := url.Values{}
	params.Add("method", "artist.getInfo")
	params.Add("artist", name)
	params.Add("mbid", mbid)
	params.Add("lang", c.lang)
	response, err := c.makeRequest(params)
	if err != nil {
		return nil, err
	}
	return &response.Artist, nil
}

func (c *Client) ArtistGetSimilar(ctx context.Context, name string, mbid string, limit int) ([]Artist, error) {
	params := url.Values{}
	params.Add("method", "artist.getSimilar")
	params.Add("artist", name)
	params.Add("mbid", mbid)
	params.Add("limit", strconv.Itoa(limit))
	response, err := c.makeRequest(params)
	if err != nil {
		return nil, err
	}
	return response.SimilarArtists.Artists, nil
}

func (c *Client) ArtistGetTopTracks(ctx context.Context, name string, mbid string, limit int) ([]Track, error) {
	params := url.Values{}
	params.Add("method", "artist.getTopTracks")
	params.Add("artist", name)
	params.Add("mbid", mbid)
	params.Add("limit", strconv.Itoa(limit))
	response, err := c.makeRequest(params)
	if err != nil {
		return nil, err
	}
	return response.TopTracks.Track, nil
}

func (c *Client) parseError(data []byte) error {
	var e Error
	err := json.Unmarshal(data, &e)
	if err != nil {
		return err
	}
	return fmt.Errorf("last.fm error(%d): %s", e.Code, e.Message)
}
