package testclient

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"net/url"
)

type requestBuilder struct {
	req *http.Request
}

func newRequestBuilder(method string) *requestBuilder {
	return &requestBuilder{
		req: &http.Request{
			Method: method,
			Header: map[string][]string{},
			URL:    &url.URL{},
		},
	}
}

func newRequestBuilderWithUrl(method string, path string) *requestBuilder {
	parsedUrl, _ := url.Parse(fmt.Sprintf(`http://localhost:3001%s`, path))
	return &requestBuilder{
		req: &http.Request{
			Method: method,
			Header: map[string][]string{},
			URL:    parsedUrl,
		},
	}
}

func (rb *requestBuilder) withJson(dataToEncode interface{}) (*requestBuilder, error) {
	var data []byte
	var err error
	switch v := dataToEncode.(type) {
	case string:
		data = []byte(v)
		err = nil
	default:
		data, err = json.Marshal(v)
	}
	if err != nil {
		return nil, err
	}

	rb.req.Body = io.NopCloser(bytes.NewReader(data))
	rb.req.Header.Add("Content-Type", "application/json")

	return rb, nil
}

func (rb *requestBuilder) withMultipartFormData(files []fileData, fields []fieldData) (*requestBuilder, error) {
	body := new(bytes.Buffer)
	writer := multipart.NewWriter(body)
	err := writeData(writer, files, fields)
	if err != nil {
		return nil, err
	}

	rb.req.Body = io.NopCloser(body)
	rb.req.Header.Add("Content-Type", writer.FormDataContentType())
	return rb, nil
}

func (rb *requestBuilder) getRequest() *http.Request {
	return rb.req
}

type fileData struct {
	field    string
	filename string
	content  []byte
}

type fieldData struct {
	field   string
	content string
}

func writeData(mw *multipart.Writer, files []fileData, fields []fieldData) error {
	err := writeFile(mw, files)
	if err != nil {
		return err
	}

	err = writeFields(mw, fields)
	if err != nil {
		return err
	}

	err = mw.Close()
	if err != nil {
		return err
	}

	return nil
}

func writeFile(mw *multipart.Writer, files []fileData) error {
	for _, file := range files {
		w, err := mw.CreateFormFile(file.field, file.filename)
		if err != nil {
			return err
		}
		_, err = w.Write(file.content)
		if err != nil {
			return err
		}
	}

	return nil
}

func writeFields(mw *multipart.Writer, files []fieldData) error {
	for _, file := range files {
		w, err := mw.CreateFormField(file.field)
		if err != nil {
			return err
		}
		_, err = w.Write([]byte(file.content))
		if err != nil {
			return err
		}
	}

	return nil
}
