package search

import (
	"fmt"
	"reflect"
	"sort"
	"strings"

	"github.com/cli/cli/v2/pkg/text"
)

const (
	KindRepositories = "repositories"
	KindIssues       = "issues"
)

type Query struct {
	Keywords   []string
	Kind       string
	Limit      int
	Order      string
	Page       int
	Qualifiers Qualifiers
	Sort       string
}

type Qualifiers struct {
	Archived            *bool
	Assignee            string
	Author              string
	Base                string
	Closed              string
	Commenter           string
	Comments            string
	Created             string
	Draft               *bool
	Followers           string
	Fork                string
	Forks               string
	GoodFirstIssues     string
	Head                string
	HelpWantedIssues    string
	In                  []string
	Interactions        string
	Involves            string
	Is                  []string
	Label               []string
	Language            string
	License             []string
	Mentions            string
	Merged              string
	Milestone           string
	No                  []string
	Project             string
	Pushed              string
	Reactions           string
	Repo                []string
	Review              string
	ReviewRequested     string
	ReviewedBy          string
	Size                string
	Stars               string
	State               string
	Status              string
	Team                string
	TeamReviewRequested string
	Topic               []string
	Topics              string
	Type                string
	Updated             string
	User                string
}

func (q Query) String() string {
	qualifiers := formatQualifiers(q.Qualifiers)
	keywords := formatKeywords(q.Keywords)
	all := append(keywords, qualifiers...)
	return strings.TrimSpace(strings.Join(all, " "))
}

func (q Qualifiers) Map() map[string][]string {
	m := map[string][]string{}
	v := reflect.ValueOf(q)
	t := reflect.TypeOf(q)
	for i := 0; i < v.NumField(); i++ {
		fieldName := t.Field(i).Name
		key := text.CamelToKebab(fieldName)
		typ := v.FieldByName(fieldName).Kind()
		value := v.FieldByName(fieldName)
		switch typ {
		case reflect.Ptr:
			if value.IsNil() {
				continue
			}
			v := reflect.Indirect(value)
			m[key] = []string{fmt.Sprintf("%v", v)}
		case reflect.Slice:
			if value.IsNil() {
				continue
			}
			s := []string{}
			for i := 0; i < value.Len(); i++ {
				if value.Index(i).IsZero() {
					continue
				}
				s = append(s, fmt.Sprintf("%v", value.Index(i)))
			}
			m[key] = s
		default:
			if value.IsZero() {
				continue
			}
			m[key] = []string{fmt.Sprintf("%v", value)}
		}
	}
	return m
}

func quote(s string) string {
	if strings.ContainsAny(s, " \"\t\r\n") {
		return fmt.Sprintf("%q", s)
	}
	return s
}

func formatQualifiers(qs Qualifiers) []string {
	var all []string
	for k, vs := range qs.Map() {
		for _, v := range vs {
			all = append(all, fmt.Sprintf("%s:%s", k, quote(v)))
		}
	}
	sort.Strings(all)
	return all
}

func formatKeywords(ks []string) []string {
	for i, k := range ks {
		ks[i] = quote(k)
	}
	return ks
}
