package qs

import (
	"net/http"
	"net/url"
)

// Page represents a combination of pagination, filter and sort parameters for, most likely, a database query.
type Page struct {
	Pagination *Pagination `json:"pagination"`
	Filters    Filters     `json:"filters,omitempty"`
	Sorts      Sorts       `json:"sorts,omitempty"`
	Joins      Joins       `json:"joins,omitempty"`
}

// ReadPageOptions configures the behaviour of ReadPage.
type ReadPageOptions struct {
	Pagination *ReadPaginationOptions
	Filter     *ReadFiltersOptions
	Sort       *ReadSortsOptions
	Join       *ReadJoinsOptions
}

// ReadPage parses URL values into a convenient Page struct.
func ReadPage(values url.Values, opt *ReadPageOptions) (*Page, error) {
	opt = initPageOptions(opt)

	pag, err := ReadPagination(values, opt.Pagination)
	if err != nil {
		return nil, err
	}

	filters, err := ReadFilters(values, opt.Filter)
	if err != nil {
		return nil, err
	}

	sorts, err := ReadSorts(values, opt.Sort)
	if err != nil {
		return nil, err
	}

	joins, err := ReadJoins(values, opt.Join)
	if err != nil {
		return nil, err
	}

	page := &Page{
		Pagination: pag,
		Filters:    filters,
		Sorts:      sorts,
		Joins:      joins,
	}
	return page, nil
}

// ReadRequestPage parses a request's query string into a convenient Page struct.
// This function always returns a value if it does not encounter an error.
func ReadRequestPage(req *http.Request, opt *ReadPageOptions) (*Page, error) {
	return ReadPage(req.URL.Query(), opt)
}

// ReadStringPage parses a query string literal into a convenient Page struct.
// This function always returns a value if it does not encounter an error.
func ReadStringPage(qs string, opt *ReadPageOptions) (*Page, error) {
	values, err := url.ParseQuery(qs)
	if err != nil {
		return nil, err
	}
	return ReadPage(values, opt)
}

func initPageOptions(opt *ReadPageOptions) *ReadPageOptions {
	def := &ReadPageOptions{}
	if opt != nil {
		def.Pagination = initPaginationOptions(opt.Pagination)
		def.Filter = initFiltersOptions(opt.Filter)
		def.Sort = initSortsOptions(opt.Sort)
	}
	return def
}