package core

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	u "net/url"
	"strconv"
	"strings"
	"time"

	"github.com/cdle/sillyplus/utils"
	"github.com/dop251/goja"
	"github.com/goccy/go-json"
)

func request(wt interface{}, handles ...func(error, map[string]interface{}, interface{}) interface{}) interface{} {
	var method = "get"
	var url = ""
	var req *http.Request

	var headers map[string]interface{}
	// var formData map[string]interface{}
	var isJson bool
	var isJsonBody bool
	var body string
	var allow_redirects bool = true
	// var useproxy bool
	// var timeout time.Duration = 0
	var goroutine = false
	var uuid = ""
	switch wt := wt.(type) {
	case string:
		url = wt
	default:
		props := wt.(map[string]interface{})
		for i := range props {
			switch strings.ToLower(i) {
			// case "timeout":
			// 	timeout = time.Duration(utils.Int64(props[i])) * time.Millisecond
			case "headers":
				headers = props[i].(map[string]interface{})
			case "method":
				method = strings.ToLower(props[i].(string))
			case "url":
				if f, ok := props[i].(func(i goja.FunctionCall) goja.Value); ok {
					url = f(goja.FunctionCall{}).ToString().String()
				} else {
					url = props[i].(string)
				}
			case "json":
				isJson = props[i].(bool)
			case "uuid":
				uuid = props[i].(string)
			case "goroutine":
				goroutine = true
			case "datatype":
				switch props[i].(type) {
				case string:
					switch strings.ToLower(props[i].(string)) {
					case "json":
						isJson = true
					}
				}
			case "allowredirects":
				allow_redirects = props[i].(bool)
			case "body":
				if v, ok := props[i].(string); !ok {
					d, _ := json.Marshal(props[i])
					body = string(d)
					isJsonBody = true
				} else {
					body = v
				}
				// case "formdata":
				// 	formData = props[i].(map[string]interface{})
				// case "useproxy":
				// 	useproxy = props[i].(bool)
			}
		}
	}
	method = strings.ToUpper(method)
	req, _ = http.NewRequest(method, url, bytes.NewBuffer([]byte(body)))
	// if timeout != 0 {
	// 	req.SetTimeout(timeout, timeout)
	// }

	if isJsonBody {
		req.Header.Set("Content-Type", "application/json")
	}
	for i := range headers {
		req.Header.Set(i, fmt.Sprint(headers[i]))
	}
	// for i := range formData {
	// 	req.Param(i, fmt.Sprint(formData[i]))
	// }

	// if useproxy && Transport != nil {
	// 	req.SetTransport(Transport)
	// }

	rspObj := map[string]interface{}{}
	var rsp *http.Response
	var err error
	// if !allow_redirects {
	// 	req.SetCheckRedirect(func(req *http.Request, via []*http.Request) error {
	// 		return http.ErrUseLastResponse
	// 	})
	// }
	var dddd = func() interface{} {
		client := &http.Client{}
		if !allow_redirects {
			client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
				return http.ErrUseLastResponse
			}
		}
		rsp, err = client.Do(req)
		var bd interface{}
		if err == nil {
			defer rsp.Body.Close()
			rspObj["status"] = rsp.StatusCode
			rspObj["statusCode"] = rsp.StatusCode
			data, _ := ioutil.ReadAll(rsp.Body)
			if isJson {
				var v interface{}
				json.Unmarshal(data, &v)
				bd = v
			} else {
				bd = string(data)
			}
			rspObj["body"] = bd
			h := make(map[string][]string)
			for k := range rsp.Header {
				h[k] = rsp.Header[k]
			}
			rspObj["headers"] = h
		} else {
			rspObj["error"] = err.Error()
		}
		if uuid != "" {
			rspObj["uuid"] = uuid
		}
		if len(handles) > 0 {
			return handles[0](err, rspObj, bd)
		} else {
			return rspObj
		}
	}
	if goroutine {
		go dddd()
	} else {
		return dddd()
	}
	return nil
}

func fetch(vm *goja.Runtime, resolve func(result interface{}), reject func(reason interface{}), wts ...interface{}) {
	var method = "get"
	var url = ""
	var req *http.Request

	var headers map[string]interface{}
	var transport *http.Transport
	var formData map[string]interface{}
	// var isJson bool
	var isJsonBody bool
	var body string
	var allow_redirects bool = true
	var responseType = ""
	// var err error
	// var useproxy bool
	var timeout time.Duration = 0
	var login = false
	for _, wt := range wts {
		switch wt := wt.(type) {
		case string:
			url = wt
		case map[string]interface{}:
			props := wt
			for i := range props {
				switch strings.ToLower(i) {
				case "timeout":
					timeout = time.Duration(utils.Int64(props[i])) * time.Millisecond
				case "headers":
					headers = props[i].(map[string]interface{})
				case "method":
					method = strings.ToLower(props[i].(string))
				case "url":
					if f, ok := props[i].(func(i goja.FunctionCall) goja.Value); ok {
						url = f(goja.FunctionCall{}).ToString().String()
					} else {
						url = props[i].(string)
					}
				case "json":
					if props[i].(bool) {
						responseType = "json"
					}
				case "responsetype":
					responseType = props[i].(string)
				case "datatype":
					responseType = props[i].(string)
				case "allowredirects":
					allow_redirects = props[i].(bool)
				case "body":
					if v, ok := props[i].(string); !ok {
						d, _ := json.Marshal(props[i])
						body = string(d)
						isJsonBody = true
					} else {
						body = v
					}
				case "login":
					login = true
				case "formdata":
					formData = props[i].(map[string]interface{})
				case "form":
					formData = props[i].(map[string]interface{})
				case "proxy":
					var url, user, password string
					for k, v := range props[i].(map[string]interface{}) {
						if k == "url" {
							url = fmt.Sprint(v)
						}
						if k == "user" {
							user = fmt.Sprint(v)
						}
						if k == "password" {
							password = fmt.Sprint(v)
						}
					}
					if url != "" {
						var err error
						transport, err = GetTransport(url, user, password)
						if err != nil {
							reject(Error(vm, "proxy config error "+err.Error()))
							return
						}
					}
				}
			}
		}
		method = strings.ToUpper(method)

		if len(formData) > 0 {
			data := u.Values{}
			for key, value := range formData {
				switch v := value.(type) {
				case string:
					data.Set(key, v)
				case bool:
					data.Set(key, strconv.FormatBool(v))
				case int:
					data.Set(key, strconv.Itoa(v))
				// 可以根据需要添加其他类型的处理
				default:
					data.Set(key, fmt.Sprintf("%v", v))
				}
			}

			req, _ = http.NewRequest(method, url, strings.NewReader(data.Encode()))
			req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
		} else {
			req, _ = http.NewRequest(method, url, bytes.NewBuffer([]byte(body)))
			if isJsonBody {
				req.Header.Set("Content-Type", "application/json")
			}
		}
		if login {
			req.Header.Set("Cookie", "uuid=40e67d5e-f6f3-11ed-8bc2-dca9049272e5; token="+getTempAuth())
		}
		for i := range headers {
			req.Header.Set(i, fmt.Sprint(headers[i]))
		}
	}

	var rspObj goja.Proxy
	var rsp *http.Response
	var err error

	var client = &http.Client{
		Timeout: timeout,
	}
	if transport != nil {
		client.Transport = transport
	}

	if !allow_redirects {
		client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
			return http.ErrUseLastResponse
		}
	}
	rsp, err = client.Do(req)
	if err == nil {
		defer rsp.Body.Close()
		rspObj = vm.NewProxy(MakeResponseObject(vm, reject, rsp, responseType), &goja.ProxyTrapConfig{
			Get: func(target *goja.Object, property string, receiver goja.Value) (value goja.Value) {
				obj := target.Get(property)
				if obj != nil {
					return obj
				}
				switch property {
				case "statusText", "statusMessage":
					return vm.ToValue("")
				case "statusCode":
					return target.Get("status")
				// case "body":
				// 	return vm.ToValue(target.Get("getBody").Export().(func() interface{})())
				case "then":
					return goja.Undefined()
				}
				console.Error("response has no property " + property)
				return goja.Undefined()
			},
		})
		resolve(rspObj)
	} else {
		reject(Error(vm, err))
	}
}
