package requests

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

// Workflow 工作流
type Workflow struct {
	session   *Session
	ParsedURL *url.URL
	Method    string
	Body      IBody
	Header    http.Header
	Cookies   map[string]*http.Cookie
}

// NewWorkflow new and init workflow
func NewWorkflow(ses *Session, u string) *Workflow {
	wf := &Workflow{}
	wf.SwitchSession(ses)

	wf.SetRawURL(u)

	wf.Body = NewBody()
	wf.Header = make(http.Header)
	wf.Cookies = make(map[string]*http.Cookie)
	return wf
}

// SwitchSession 替换Session
func (wf *Workflow) SwitchSession(ses *Session) {
	wf.session = ses
}

// AddHeader 添加头信息  Get方法从Header参数上获取
func (wf *Workflow) AddHeader(key, value string) *Workflow {
	wf.Header.Add(key, value)
	return wf
}

// SetHeader 设置完全替换原有Header
func (wf *Workflow) SetHeader(header http.Header) *Workflow {
	wf.Header = make(http.Header)
	for k, HValues := range header {
		var newHValues []string
		for _, value := range HValues {
			newHValues = append(newHValues, value)
		}
		wf.Header[k] = newHValues
	}
	return wf
}

// GetHeader 获取Workflow Header
func (wf *Workflow) GetHeader() http.Header {
	return wf.Header
}

// GetCombineHeader 获取后的Header信息
func (wf *Workflow) GetCombineHeader() http.Header {
	return mergeMapList(wf.session.Header, wf.Header)
}

// DelHeader 添加头信息 Get方法从Header参数上获取
func (wf *Workflow) DelHeader(key string) *Workflow {
	wf.Header.Del(key)
	return wf
}

// AddCookie 添加Cookie
func (wf *Workflow) AddCookie(c *http.Cookie) *Workflow {
	wf.Cookies[c.Name] = c
	return wf
}

// AddCookies 添加[]*http.Cookie
func (wf *Workflow) AddCookies(cookies []*http.Cookie) *Workflow {
	for _, c := range cookies {
		wf.AddCookie(c)
	}
	return wf
}

// AddKVCookie 添加 以 key value 的 Cookie
func (wf *Workflow) AddKVCookie(name, value string) *Workflow {
	wf.Cookies[name] = &http.Cookie{Name: name, Value: value}
	return wf
}

// DelCookie 删除Cookie
func (wf *Workflow) DelCookie(name interface{}) *Workflow {
	switch n := name.(type) {
	case string:
		if _, ok := wf.Cookies[n]; ok {
			delete(wf.Cookies, n)
			return wf
		}
	case *http.Cookie:
		if _, ok := wf.Cookies[n.Name]; ok {
			delete(wf.Cookies, n.Name)
			return wf
		}
	default:
		panic("name type is not support")
	}
	return nil
}

// GetParsedURL 获取url的string形式
func (wf *Workflow) GetParsedURL() *url.URL {
	return wf.ParsedURL
}

// SetParsedURL 获取url的string形式
func (wf *Workflow) SetParsedURL(u *url.URL) *Workflow {
	wf.ParsedURL = u
	return wf
}

// GetRawURL 获取url的string形式
func (wf *Workflow) GetRawURL() string {
	u := strings.Split(wf.ParsedURL.String(), "?")[0] + "?" + wf.GetCombineQuery().Encode()
	return u
}

// SetRawURL 设置 url
func (wf *Workflow) SetRawURL(srcURL string) *Workflow {
	purl, err := url.ParseRequestURI(srcURL)
	if err != nil {
		panic(err)
	}
	wf.ParsedURL = purl
	return wf
}

// GetQuery 获取Query参数
func (wf *Workflow) GetQuery() url.Values {
	return wf.ParsedURL.Query()
}

// GetCombineQuery 获取Query参数
func (wf *Workflow) GetCombineQuery() url.Values {
	if wf.ParsedURL != nil {
		vs := wf.ParsedURL.Query()
		return mergeMapList(wf.session.GetQuery(), vs)
	}
	return nil
}

// SetQuery 设置Query参数
func (wf *Workflow) SetQuery(query url.Values) *Workflow {
	if query == nil {
		return wf
	}
	query = (url.Values)(mergeMapList(wf.session.Query, query))
	wf.ParsedURL.RawQuery = query.Encode()
	return wf
}

var regexGetPath = regexp.MustCompile("/[^/]*")

// GetURLPath 获取Path参数
func (wf *Workflow) GetURLPath() []string {
	return regexGetPath.FindAllString(wf.ParsedURL.Path, -1)
}

// GetURLRawPath 获取未分解Path参数
func (wf *Workflow) GetURLRawPath() string {
	return wf.ParsedURL.Path
}

// encodePath path格式每个item都必须以/开头
func encodePath(path []string) string {
	rawpath := ""
	for _, p := range path {
		if p[0] != '/' {
			p = "/" + p
		}
		rawpath += p
	}
	return rawpath
}

// SetURLPath 设置Path参数
func (wf *Workflow) SetURLPath(path []string) *Workflow {
	if path == nil {
		return wf
	}
	wf.ParsedURL.Path = encodePath(path)
	return wf
}

// SetURLRawPath 设置Pa晚上参数
func (wf *Workflow) SetURLRawPath(path string) *Workflow {
	wf.ParsedURL.Path = path
	return wf
}

// SetBody 参数设置
func (wf *Workflow) SetBody(body IBody) *Workflow {
	wf.Body = body
	return wf
}

// GetBody 参数设置
func (wf *Workflow) GetBody(body IBody) IBody {
	return wf.Body
}

// AutoSetBody 参数设置
func (wf *Workflow) AutoSetBody(params ...interface{}) *Workflow {

	if params != nil {
		plen := len(params)
		defaultContentType := TypeURLENCODED

		if plen >= 2 {
			t := params[plen-1]
			defaultContentType = t.(string)
		}

		wf.Body.SetPrefix(defaultContentType)

		if defaultContentType == TypeFormData {
			createMultipart(wf.Body, params)
		} else {
			var values url.Values
			switch param := params[0].(type) {
			case map[string]string:
				values := make(url.Values)
				for k, v := range param {
					values.Set(k, v)
				}
				wf.Body.SetIOBody([]byte(values.Encode()))
			case map[string][]string:
				values = param
				wf.Body.SetIOBody([]byte(values.Encode()))
			case string:
				wf.Body.SetIOBody([]byte(param))
			case []byte:
				wf.Body.SetIOBody(param)
			}

		}
	}
	return wf
}

func mergeMapList(headers ...map[string][]string) map[string][]string {

	set := make(map[string]map[string]int)
	merged := make(map[string][]string)

	for _, header := range headers {
		for key, values := range header {
			for _, v := range values {
				if vs, ok := set[key]; ok {
					vs[v] = 1
				} else {
					set[key] = make(map[string]int)
					set[key][v] = 1
				}
			}
		}
	}

	for key, mvalue := range set {
		for v := range mvalue {
			// merged.Add(key, v)
			if mergeValue, ok := merged[key]; ok {
				merged[key] = append(mergeValue, v)
			} else {
				merged[key] = []string{v}
			}
		}
	}

	return merged
}

// setHeaderRequest 设置request的头
func setHeaderRequest(req *http.Request, wf *Workflow) {
	req.Header = mergeMapList(req.Header, wf.session.Header, wf.Header)
}

// setHeaderRequest 设置request的临时Cookie, 永久需要在session上设置cookie
func setTempCookieRequest(req *http.Request, wf *Workflow) {
	if wf.Cookies != nil {
		for _, c := range wf.Cookies {
			req.AddCookie(c)
		}
	}
}

// Execute 执行
func (wf *Workflow) Execute() (*Response, error) {

	req := buildBodyRequest(wf)

	setHeaderRequest(req, wf)
	setTempCookieRequest(req, wf)

	if wf.session.auth != nil {
		req.SetBasicAuth(wf.session.auth.User, wf.session.auth.Password)
	}

	resp, err := wf.session.client.Do(req)
	if err != nil {
		return nil, err
	}

	return FromHTTPResponse(resp)
}
