package ytsclient

import (
	"context"
	"fmt"
	"net"
	"strconv"
	"strings"
	"time"

	r "github.com/levigross/grequests"
)

type client struct {
	url         string
	list        string
	get         string
	suggestions string
}

var (
	defaultHost = "https://yts.am"
	apiPrefix   = "/api/v2"
	dnsServer   = "8.8.4.4"
)

const (
	list        = "/list_movies.json"
	get         = "/movie_details.json"
	suggestions = "/movie_suggestions.json"
)


func NewClient(host ...string) Client {
	if len(host) == 0 || host[0] == "" {
		if host, err := ResolveYts(); err == nil {
			defaultHost = strings.TrimSuffix(host, "/")
		}
		host = []string{defaultHost}
	}
	d := net.Dialer{
		Timeout: 5 * time.Second,
	}
	net.DefaultResolver = &net.Resolver{
		PreferGo:     true,
		Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
			return d.DialContext(ctx, network, fmt.Sprintf("%s:53", dnsServer))
		},
	}
	return &client{host[0], host[0] + apiPrefix + list, host[0] + apiPrefix + get, host[0] + apiPrefix + suggestions}
}

func (y *client) List(params *ListParams) ([]Movie, error) {
	if params == nil {
		params = &ListParams{}
	}
	res, err := r.Get(y.list, params.format())
	if err != nil {
		return nil, err
	}
	var resp ListResponse
	if err := res.JSON(&resp); err != nil {
		return nil, err
	}
	return resp.Data.Movies, nil
}

func (y *client) Search(search string, params *ListParams) ([]Movie, error) {
	if params == nil {
		params = &ListParams{}
	}
	params.QueryTerm = search
	res, err := r.Get(y.list, params.format())
	if err != nil {
		return nil, err
	}
	var resp ListResponse
	if err := res.JSON(&resp); err != nil {
		return nil, err
	}
	return resp.Data.Movies, nil
}

func (y *client) Get(id string, params *MovieParams) (Movie, error) {
	if params == nil {
		params = &MovieParams{}
	}
	p := params.format()
	p.Params["movie_id"] = id
	res, err := r.Get(y.get, p)
	if err != nil {
		return Movie{}, err
	}
	var resp MovieResponse
	if err := res.JSON(&resp); err != nil {
		return Movie{}, err
	}
	return resp.Data.Movie, nil
}

func (y *client) Suggestions(id int) ([]Movie, error) {
	ro := &r.RequestOptions{
		Params: map[string]string{"movie_id": strconv.Itoa(id)},
	}
	res, err := r.Get(y.suggestions, ro)
	if err != nil {
		return nil, err
	}
	var resp ListResponse
	if err := res.JSON(&resp); err != nil {
		return nil, err
	}
	return resp.Data.Movies, nil
}

type Client interface {
	List(params *ListParams) ([]Movie, error)
	Search(search string, params *ListParams) ([]Movie, error)
	Get(id string, params *MovieParams) (Movie, error)
	Suggestions(id int) ([]Movie, error)
}
