package Api

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"net/http/httptest"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strconv"
	"testing"

	"git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Database"
	"git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Models"
	"gorm.io/gorm"

	"github.com/gorilla/mux"
)

func init() {
	// Fix working directory for tests
	_, filename, _, _ := runtime.Caller(0)
	dir := path.Join(path.Dir(filename), "..")
	err := os.Chdir(dir)
	if err != nil {
		panic(err)
	}

	log.SetOutput(ioutil.Discard)
	Database.Init()

	r = mux.NewRouter()
}

func get(url string) (resp *http.Response, err error) {
	for i := 0; i < 5; i++ {
		resp, err = http.Get(url)
		if err == nil {
			return resp, err
		}
	}

	return resp, err
}

// Image generates a *os.File with a random image using the loremflickr.com service
func fakeImage(width, height int, categories []string, prefix string, categoriesStrict bool) *os.File {
	url := "https://loremflickr.com"

	switch prefix {
	case "g":
		url += "/g"
	case "p":
		url += "/p"
	case "red":
		url += "/red"
	case "green":
		url += "/green"
	case "blue":
		url += "/blue"
	}

	url += string('/') + strconv.Itoa(width) + string('/') + strconv.Itoa(height)

	if len(categories) > 0 {

		url += string('/')

		for _, category := range categories {
			url += category + string(',')
		}

		if categoriesStrict {
			url += "/all"
		}
	}

	resp, err := get(url)
	defer resp.Body.Close()

	if err != nil {
		panic(err)
	}

	f, err := ioutil.TempFile(os.TempDir(), "loremflickr-img-*.jpg")
	if err != nil {
		panic(err)
	}

	io.Copy(f, resp.Body)

	err = f.Close()
	if err != nil {
		panic(err)
	}

	return f
}

func Test_createPostImages(t *testing.T) {
	t.Log("Testing createPostImages...")

	r.HandleFunc("/post/{postID}/image", createPostImage).Methods("POST")

	ts := httptest.NewServer(r)
	defer ts.Close()

	postData := Models.Post{
		Title:     "Test post",
		Content:   "Test content",
		FrontPage: true,
		Order:     1,
		PostLinks: []Models.PostLink{
			{
				Type: "Facebook",
				Link: "http://facebook.com/",
			},
		},
	}

	err := Database.CreatePost(&postData)
	if err != nil {
		t.Errorf("Expected nil, recieved %s", err.Error())
	}

	image := fakeImage(100, 100, []string{}, "", false)

	image, err = os.Open(image.Name())
	if err != nil {
		t.Errorf("Expected nil, recieved %s", err.Error())
	}

	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	part, err := writer.CreateFormFile("files", filepath.Base(image.Name()))

	if err != nil {
		t.Errorf("Expected nil, recieved %s", err.Error())
	}

	io.Copy(part, image)
	writer.Close()
	request, err := http.NewRequest(
		"POST",
		fmt.Sprintf(
			"%s/post/%s/image",
			ts.URL,
			postData.ID,
		),
		body,
	)

	if err != nil {
		t.Errorf("Expected nil, recieved %s", err.Error())
	}

	request.Header.Add("Content-Type", writer.FormDataContentType())
	client := &http.Client{}

	res, err := client.Do(request)
	if err != nil {
		t.Errorf("Expected nil, recieved %s", err.Error())
	}
	if res.StatusCode != http.StatusOK {
		t.Errorf("Expected %d, recieved %d", http.StatusOK, res.StatusCode)
	}

	defer res.Body.Close()

	updatePostData := new(Models.Post)
	err = json.NewDecoder(res.Body).Decode(updatePostData)
	if err != nil {
		t.Errorf("Expected nil, recieved %s", err.Error())
	}

	defer Database.DB.
		Session(&gorm.Session{FullSaveAssociations: true}).
		Unscoped().
		Select("PostLinks", "PostImages").
		Delete(&postData)

	if len(updatePostData.PostImages) != 1 {
		t.Errorf("Expected len(updatePostData.PostImages) == 1, recieved %d", len(updatePostData.PostImages))
	}

	for _, f := range updatePostData.PostImages {
		if _, err := os.Stat("./" + f.Filepath); errors.Is(err, os.ErrNotExist) {
			t.Errorf(
				"File ./%s does not exist",
				f.Filepath,
			)
		} else {
			os.Remove("./" + f.Filepath)
		}
	}
}

func Test_deletePostImages(t *testing.T) {
	t.Log("Testing createPostImages...")

	r.HandleFunc("/post/{postID}/image/{imageID}", deletePostImage).Methods("DELETE")

	ts := httptest.NewServer(r)
	defer ts.Close()

	image := fakeImage(100, 100, []string{}, "", false)

	image, err := os.Open(image.Name())
	if err != nil {
		t.Errorf("Expected nil, recieved %s", err.Error())
	}

	defer image.Close()

	postData := Models.Post{
		Title:     "Test post",
		Content:   "Test content",
		FrontPage: true,
		Order:     1,
		PostLinks: []Models.PostLink{
			{
				Type: "Facebook",
				Link: "http://facebook.com/",
			},
		},
		PostImages: []Models.PostImage{
			{
				Filepath: image.Name(),
			},
		},
	}

	err = Database.CreatePost(&postData)
	if err != nil {
		t.Errorf("Expected nil, recieved %s", err.Error())
	}

	defer Database.DB.
		Session(&gorm.Session{FullSaveAssociations: true}).
		Unscoped().
		Select("PostLinks", "PostImages").
		Delete(&postData)

	req, err := http.NewRequest("DELETE", fmt.Sprintf(
		"%s/post/%s/image/%s",
		ts.URL,
		postData.ID,
		postData.PostImages[0].ID,
	), nil)

	if err != nil {
		t.Errorf("Expected nil, recieved %s", err.Error())
	}

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		t.Errorf("Expected nil, recieved %s", err.Error())
		return
	}
	defer res.Body.Close()

	if res.StatusCode != http.StatusOK {
		t.Errorf("Expected %d, recieved %d", http.StatusOK, res.StatusCode)
	}
}
