package admin

import (
	"io/ioutil"
	"log"
	"net/http"
	"path/filepath"
	"strconv"

	"gitlab.thpeetz.de/kontor/kontor-go/pkg/auth"
	"gitlab.thpeetz.de/kontor/kontor-go/pkg/comics"
	"gitlab.thpeetz.de/kontor/kontor-go/pkg/dao"
	"gitlab.thpeetz.de/kontor/kontor-go/pkg/util"

	"github.com/gin-gonic/gin"
	"github.com/smallfish/simpleyaml"
)

const (
	// KontorUserAdministrationTitle defines the text of the page title
	KontorUserAdministrationTitle = "Kontor User Administration"
	// DataUploadTemplate defines the name of the template file for the data upload
	DataUploadTemplate = "kontor/data-upload.html"
)

// ShowLoginPage renders login page.
func ShowLoginPage(c *gin.Context) {
	// Call the render function with the name of the template to render
	util.Render(c, gin.H{"title": "Login"}, "login.html")
}

// PerformLogin reads data from login form and validates input.
func PerformLogin(c *gin.Context) {
	// Obtain the POSTed username and password values
	username := c.PostForm("username")
	password := c.PostForm("password")

	var userDao = UserDAO{Db: dao.KontorDb}

	// Check if the username/password combination is valid
	if userDao.IsUserValid(username, password) {
		sessionInterface, _ := c.Get("session")
		sessionID := sessionInterface.(string)
		user, _ := userDao.FindByUsername(username)
		sessionDao := auth.SessionDAO{Db: dao.KontorDb}
		session, _ := sessionDao.FindByID(sessionID)
		session.Username = username
		session.IsAdmin = user.IsAdmin
		sessionDao.Update(session)
		util.Render(c, gin.H{"title": "Successful Login", "InfoMessage": "Login successfull"}, "kontor/index.html")
	} else {
		// If the username/password combination is invalid,
		// show the error message on the login page
		c.HTML(http.StatusBadRequest, "login.html", gin.H{
			"ErrorTitle":   "Login Failed",
			"ErrorMessage": "Invalid credentials provided"})
	}
}

// Logout invalidates session.
func Logout(c *gin.Context) {
	sessionInterface, _ := c.Get("session")
	sessionID := sessionInterface.(string)
	c.SetCookie("session", sessionID, -1, "", "", false, true)

	// Redirect to the home page
	c.Redirect(http.StatusTemporaryRedirect, "/")
}

func showAdminIndex(c *gin.Context) {
	// Call the render function with the name of the template to render
	util.Render(c, gin.H{"title": "Kontor", "payload": nil}, "kontor/admin.html")
}

func showUserIndex(c *gin.Context) {
	var dao = UserDAO{Db: dao.KontorDb}
	if users, err := dao.FindAll(); err == nil && users != nil {
		util.Render(c, gin.H{"title": KontorUserAdministrationTitle, "payload": users}, "kontor/users.html")
	} else {
		util.Render(c, gin.H{"title": KontorUserAdministrationTitle, "payload": users, "ErrorMessage": err}, "kontor/users.html")
	}
}

func showUserDetails(c *gin.Context) {
	userID := c.Param("userid")
	var userDao = UserDAO{Db: dao.KontorDb}
	if user, err := userDao.FindByID(userID); err == nil && &user != nil {
		util.Render(c, gin.H{"title": KontorUserAdministrationTitle, "payload": user, "action": util.SaveAction}, "kontor/user-detail.html")
	} else {
		c.AbortWithError(http.StatusNotFound, err)
	}
}

func showUserCreation(c *gin.Context) {
	var user = User{}
	util.Render(c, gin.H{"title": KontorUserAdministrationTitle, "payload": user, "action": util.AddAction}, "kontor/user-detail.html")
}

func validateUserCreation(c *gin.Context) {
	// Obtain the POSTed username and password values
	username := c.PostForm("username")
	firstname := c.PostForm("firstname")
	lastname := c.PostForm("lastname")
	password := c.PostForm("password")
	adminFormVar := c.PostForm("admin")
	action := c.PostForm("action")
	userid := c.PostForm("userid")
	isAdmin, _ := strconv.ParseBool(adminFormVar)

	var err error
	var dao = UserDAO{Db: dao.KontorDb}
	var user = User{}

	switch action {
	case util.AddAction:
		user.Username = username
		user.Firstname = firstname
		user.Lastname = lastname
		user.IsAdmin = isAdmin
		user.Password, _ = HashPassword(password)
		_, err = dao.Upsert(user)
	case util.SaveAction:
		user, _ = dao.FindByID(userid)
		user.Username = username
		user.Firstname = firstname
		user.Lastname = lastname
		user.IsAdmin = isAdmin
		user.Password, _ = HashPassword(password)
		err = dao.Update(user)
	case util.DeleteAction:
		user, _ = dao.FindByID(userid)
		err = dao.Delete(user)
	}
	if err == nil {
		c.Redirect(http.StatusTemporaryRedirect, "/admin/user")
	} else {
		c.HTML(http.StatusBadRequest, "kontor/create-user.html", gin.H{
			"ErrorTitle":   "User Creation Failed",
			"ErrorMessage": err.Error()})
	}
}

func showDataUpload(c *gin.Context) {
	// Call the render function with the name of the template to render
	//util.Render(c, gin.H{"title": "Kontor", "payload": nil}, "kontor/admin.html")
	util.Render(c, gin.H{"title": "Kontor Data Upload", "payload": nil}, DataUploadTemplate)
}

func validateDataUpload(c *gin.Context) {
	// Call the render function with the name of the template to render
	//util.Render(c, gin.H{"title": "Kontor", "payload": nil}, "kontor/admin.html")
	datafile, err := c.FormFile("datafile")
	if err != nil {
		c.HTML(http.StatusBadRequest, DataUploadTemplate, gin.H{
			"ErrorTitle":   "Data Upload Failed",
			"ErrorMessage": err.Error()})
		return
	}
	log.Printf("Data File: %v", datafile.Filename)
	filename := filepath.Base(datafile.Filename)
	if err := c.SaveUploadedFile(datafile, filename); err != nil {
		c.HTML(http.StatusBadRequest, DataUploadTemplate, gin.H{
			"ErrorTitle":   "Data Upload Failed",
			"ErrorMessage": err.Error()})
		return
	}
	source, err := ioutil.ReadFile(filename)
	if err != nil {
		panic(err)
	}
	yaml, err := simpleyaml.NewYaml(source)
	if err != nil {
		panic(err)
	}
	if yaml.IsArray() {
		size, err := yaml.GetArraySize()
		if err != nil {
			panic(err)
		}
		log.Printf("Found %d entries.\n", size)
		var publisherDao comics.PublisherDAO
		publisherDao.Db = dao.KontorDb
		var artistDao comics.ArtistDAO
		artistDao.Db = dao.KontorDb
		var comicDao comics.ComicDAO
		comicDao.Db = dao.KontorDb
		publisherMap := make(map[int]string)
		for index := 0; index < size; index++ {
			entry := yaml.GetIndex(index)
			if entry.IsMap() {
				model, err := entry.Get("model").String()
				if err != nil {
					panic(err)
				}
				pk, _ := entry.Get("pk").Int()
				switch model {
				case "comics.publisher":
					name, err := entry.Get("fields").Get("name").String()
					if err != nil {
						panic(err)
					}
					log.Printf("          %v %v %v\n", pk, model, name)
					publisherMap[pk] = name
					publisher := comics.Publisher{}
					publisher.Name = name
					info, _ := publisherDao.Upsert(publisher)
					log.Printf("Publisher records changed: %d", info.Updated)
				case "comics.artist":
					name, err := entry.Get("fields").Get("name").String()
					if err != nil {
						panic(err)
					}
					log.Printf("          %v %v %v\n", pk, model, name)
					artist := comics.Artist{}
					artist.Name = name
					info, _ := artistDao.Upsert(artist)
					log.Printf("Artist records changed: %d", info.Updated)
				case "comics.comic":
					title, err := entry.Get("fields").Get("title").String()
					if err != nil {
						panic(err)
					}
					publisherID, err := entry.Get("fields").Get("publisher").Int()
					publisher, err := publisherDao.FindByName(publisherMap[publisherID])
					completed, err := entry.Get("fields").Get("completed").Bool()
					if err != nil {
						log.Printf("Error occured: %v", err)
					}
					log.Printf("          %v %v %v %v\n", pk, model, title, completed)
					comic := comics.Comic{}
					comic.Title = title
					comic.Completed = completed
					comic.Publisher = publisher.ID
					info, _ := comicDao.Upsert(comic)
					log.Printf("Comic records changed: %d", info.Updated)
				}
				//fmt.Printf("Entry %d: %v\n", index, entry)
			}
		}
	}
	util.Render(c, gin.H{"title": "Kontor Data Upload", "payload": nil}, DataUploadTemplate)
}
