package Api

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"os"
	"path"
	"runtime"
	"strings"
	"testing"

	"git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Database"
	"git.tovijaeschke.xyz/tovi/SuddenImpactRecords/Models"
	"github.com/gorilla/mux"
	"gorm.io/gorm"
)

var (
	r *mux.Router
)

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 Test_getPosts(t *testing.T) {
	t.Log("Testing getPosts...")

	r.HandleFunc("/post", getPosts).Methods("GET")

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

	var err error
	for i := 0; i < 20; i++ {
		postData := Models.Post{
			Title:     "Test post",
			Content:   "Test content",
			FrontPage: true,
			Order:     i,
			PostLinks: []Models.PostLink{
				{
					Type: "Facebook",
					Link: "http://facebook.com/",
				},
			},
		}

		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").
			Delete(&postData)
	}

	res, err := http.Get(ts.URL + "/post?page=1&pageSize=10")

	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)
	}

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

	if len(*getPostsData) != 10 {
		t.Errorf("Expected 10, recieved %d", len(*getPostsData))
	}
}

func Test_getPost(t *testing.T) {
	t.Log("Testing getPost...")

	r.HandleFunc("/post/{postID}", getPost).Methods("GET")

	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())
	}

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

	res, err := http.Get(fmt.Sprintf(
		"%s/post/%s",
		ts.URL,
		postData.ID,
	))

	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)
	}

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

func Test_createPost(t *testing.T) {
	t.Log("Testing createPost...")

	r.HandleFunc("/post", createPost).Methods("POST")

	ts := httptest.NewServer(r)

	defer ts.Close()

	postJson := `
{
	"title": "Test post",
	"content": "Test content",
	"front_page": true,
	"order": 1,
	"links": [{
		"type": "Facebook",
		"link": "http://facebook.com/"
	}]
}
`

	res, err := http.Post(ts.URL+"/post", "application/json", strings.NewReader(postJson))
	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)
	}

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

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

	if postData.Title != "Test post" {
		t.Errorf("Expected title \"Test post\", recieved \"%s\"", postData.Title)
	}
	if postData.Content != "Test content" {
		t.Errorf("Expected content \"Test content\", recieved \"%s\"", postData.Content)
	}
}

func Test_deletePost(t *testing.T) {
	t.Log("Testing deletePost...")

	r.HandleFunc("/post/{postID}", deletePost).Methods("DELETE")

	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())
	}

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

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

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

	// Fetch Request
	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)
	}
}

func Test_updatePost(t *testing.T) {
	t.Log("Testing updatePost...")

	r.HandleFunc("/post/{postID}", updatePost).Methods("PUT")

	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())
	}

	postJson := `
{
	"content": "New test content",
	"front_page": false,
	"order": 2
}
`

	req, err := http.NewRequest("PUT", fmt.Sprintf(
		"%s/post/%s",
		ts.URL,
		postData.ID,
	), strings.NewReader(postJson))

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

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

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

	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").
		Delete(&postData)

	if updatePostData.Content != "New test content" {
		t.Errorf("Expected \"New test content\", recieved %s", updatePostData.Content)
	}

	if updatePostData.FrontPage != false {
		t.Errorf("Expected false, recieved %t", updatePostData.FrontPage)
	}

	if updatePostData.Order != 2 {
		t.Errorf("Expected 2, recieved %d", updatePostData.Order)
	}
}
