package test

import (
	"bufio"
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"os"
	"strings"
	"testing"
	"time"

	"heruvym/app"
	"heruvym/model"
	"heruvym/service"

	"bitbucket.org/skeris/profile/jwt_adapter"
	"github.com/stretchr/testify/assert"
	"github.com/themakers/bdd"
)

func TestTicket(t *testing.T) {
	fmt.Println("start test")
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	bdd.Scenario(t, "SupportChat", func(t *testing.T, runID string) {
		go func() {

			time.Sleep(3 * time.Second)
			fmt.Println("start service")
			_, err := app.New(ctx, app.Options{
				MongoURI:         "mongodb://mongo:27017",
				NumberPortLocal:  "1488",
				LoggerDevMode:    false,
				MinioEndpoint:    "minio:9000",
				MinioAccessKey:   "minioadmin",
				MinioSecretKey:   "minioadmin",
				MongoDbTable:     "profile",
				MongoCollections: "profile,role",
			})
			if err != nil {
				panic(err)
			}
		}()
		time.Sleep(time.Second * 5)

		bdd.Act(t, "client", func() {
			fmt.Println("client")
			var (
				token  string
				ticket service.CreateTicketResp
				msgs   = make(chan []byte)
			)
			bdd.Test(t, "CreateTicket", func() {

				buf, err := json.Marshal(service.CreateTicketReq{
					Title:   "TestTitle",
					Message: "test",
				})
				if err != nil {
					panic(err)
				}
				req, err := http.NewRequestWithContext(
					ctx,
					"POST",
					"http://localhost:1488/support/create", bytes.NewBuffer(buf))
				if err != nil {
					panic(err)
				}

				req.Header.Add(jwt_adapter.DefaultHeaderKey, "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJRCI6ImMxcDM0YjhqZHBmOG04Zm43cnIwIiwiU2Vzc2lvbiI6ImMxcDM0YjhqZHBmOG04Zm43cnJnIiwiVXNlciI6IiIsIlRhcmlmZiI6MCwiQ3JlYXRlZCI6MTYxODA5NjY4NTc4MCwiTGFzdFNlZW4iOjE2MTgwOTY2ODU3ODF9.ciJoJiOxzIPv0LY4h3rG8Tf3AsSBXXLcYEpyN9mIki0")

				resp, err := http.DefaultClient.Do(req)

				if err != nil {
					assert.NoError(t, err)
				}

				if resp.StatusCode != http.StatusOK {
					panic("NotAccepted")
				}

				token = resp.Header.Get(jwt_adapter.DefaultHeaderKey)

				assert.NoError(t, json.NewDecoder(resp.Body).Decode(&ticket))
			})
			bdd.Test(t, "subscribe for tiket", func() {
				client := &http.Client{}

				r, err := http.NewRequest("GET", "http://localhost:1488/support/ticket?Authorization="+token+"&ticket="+ticket.Ticket, nil)
				assert.NoError(t, err)

				r.Header.Set("Accept", "text/event-stream")
				r.Header.Set("Cache-Control", "no-cache")
				r.Header.Set("Connection", "keep-alive")
				resp, err := client.Do(r)
				assert.NoError(t, err)

				assert.Equal(t, http.StatusOK, resp.StatusCode)

				sc := bufio.NewScanner(resp.Body)
				split := func(data []byte, atEOF bool) (int, []byte, error) {
					if atEOF && len(data) == 0 {
						return 0, nil, nil
					}

					if strings.Contains(string(data), ":ping") {
						return 0, nil, nil
					}

					if len(data) <= 6 {
						return 0, nil, errors.New(fmt.Sprintf("got bad body: %v", string(data)))
					}
					// Мы получили данные для обработки
					// Начинаем с 6 байта, чтобы убрать "data: "

					if i, nlen := containsDoubleNewline(data); i >= 0 {
						return i + nlen, data[6:i], nil
					}

					// Если получен EOF, то мы получили все что необходимо
					if atEOF {
						return len(data), data, nil
					}

					return 0, nil, nil
				}
				sc.Split(split)

				go func() {
					for sc.Scan() {
						body := sc.Bytes()
						msgs <- body
						fmt.Println("data from", string(body))
					}

					if err = sc.Err(); err != nil {
						fmt.Println("scanner.Err()", err)
						if err != bufio.ErrTooLong {
							fmt.Println("toolong")
						}
					}
				}()

			})
			bdd.Test(t, "send message", func() {

				buf, err := json.Marshal(service.ReqPutMessage{
					Message:  "test",
					TicketID: ticket.Ticket,
					Lang:     "ru",
				})
				if err != nil {
					panic(err)
				}

				req, err := http.NewRequestWithContext(ctx, http.MethodPost, "http://localhost:1488/support/send", bytes.NewBuffer(buf))
				assert.NoError(t, err)

				req.Header.Add(jwt_adapter.DefaultHeaderKey, token)
				resp, err := http.DefaultClient.Do(req)
				assert.NoError(t, err)

				assert.Equal(t, http.StatusOK, resp.StatusCode)

				var msg, channed model.Message
				assert.NoError(t, json.NewDecoder(resp.Body).Decode(&msg))
				err = json.Unmarshal(<-msgs, &channed)
				assert.NoError(t, err)
				assert.Equal(t, "test", channed.Message)
			})
			bdd.Test(t, "send file", func() {
				body := &bytes.Buffer{}
				writer := multipart.NewWriter(body)

				partf, _ := writer.CreateFormFile("f.go", "f.go")
				f, err := os.OpenFile("./main_test.go", os.O_RDWR, 0644)
				assert.NoError(t, err)
				io.Copy(partf, f)

				assert.NoError(t, writer.WriteField("ticket", ticket.Ticket))

				writer.Close()
				req, err := http.NewRequestWithContext(ctx, http.MethodPost, "http://localhost:1488/support/sendFiles", body)
				req.Header.Set("Content-Type", "multipart/form-data; boundary="+writer.Boundary())

				req.Header.Add(jwt_adapter.DefaultHeaderKey, token)

				resp, err := http.DefaultClient.Do(req)
				assert.NoError(t, err)
				assert.Equal(t, http.StatusOK, resp.StatusCode)

				str, err := ioutil.ReadAll(resp.Body)
				fmt.Println("resp files", err, string(str))
			})
		})
	})
}
func containsDoubleNewline(data []byte) (int, int) {
	// Ищем все возможные переносы строки
	crcr := bytes.Index(data, []byte("\r\r"))
	lflf := bytes.Index(data, []byte("\n\n"))
	crlflf := bytes.Index(data, []byte("\r\n\n"))
	lfcrlf := bytes.Index(data, []byte("\n\r\n"))
	crlfcrlf := bytes.Index(data, []byte("\r\n\r\n"))
	// Находим первый двойной перенос строки
	minPos := minPosInt(crcr, minPosInt(lflf, minPosInt(crlflf, minPosInt(lfcrlf, crlfcrlf))))
	// Определяем длину
	nlen := 2
	if minPos == crlfcrlf {
		nlen = 4
	} else if minPos == crlflf || minPos == lfcrlf {
		nlen = 3
	}
	return minPos, nlen
}

func minPosInt(a, b int) int {
	if a < 0 {
		return b
	}
	if b < 0 {
		return a
	}
	if a > b {
		return b
	}
	return a
}
