package frontend

import (
	"fmt"
	"html/template"
	"log"
	"net/http"

	b "streifling.com/jason/cpolis/cmd/backend"
)

func saveSession(w http.ResponseWriter, r *http.Request, s *b.CookieStore, u *b.User) error {
	session, err := s.Get(r, "cookie")
	if err != nil {
		return fmt.Errorf("error getting session: %v", err)
	}

	session.Values["authenticated"] = true
	session.Values["id"] = u.ID
	session.Values["name"] = u.FirstName + u.LastName
	session.Values["role"] = u.Role
	if err := session.Save(r, w); err != nil {
		return fmt.Errorf("error saving session: %v", err)
	}

	return nil
}

// getSession is used for verifying that the user is logged in and returns their session and an error.
func getSession(w http.ResponseWriter, r *http.Request, c *b.Config, s *b.CookieStore) (*b.Session, error) {
	msg := "Keine gültige Session. Bitte erneut anmelden."
	tmpl, tmplErr := template.ParseFiles(c.WebDir+"/templates/index.html", c.WebDir+"/templates/login.html")

	tmpSession, err := s.Get(r, "cookie")
	if err != nil {
		if err = template.Must(tmpl, tmplErr).ExecuteTemplate(w, "page-content", msg); err != nil {
			return nil, fmt.Errorf("error executing template: %v", err)
		}
		return nil, fmt.Errorf("error getting session: %v", err)
	}

	session := &b.Session{Session: *tmpSession}
	if session.IsNew {
		if err = template.Must(tmpl, tmplErr).ExecuteTemplate(w, "page-content", msg); err != nil {
			return nil, fmt.Errorf("error executing template: %v", err)
		}
	}

	return session, nil
}

func HomePage(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		numRows, err := db.CountEntries("users")
		if err != nil {
			log.Fatalln(err)
		}

		files := make([]string, 2)
		files[0] = c.WebDir + "/templates/index.html"
		if numRows == 0 {
			files[1] = c.WebDir + "/templates/first-user.html"
			tmpl, err := template.ParseFiles(files...)
			if err = template.Must(tmpl, err).Execute(w, nil); err != nil {
				log.Println(err)
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
		} else {
			session, err := s.Get(r, "cookie")
			if err != nil {
				log.Println(err)
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			if auth, ok := session.Values["authenticated"].(bool); auth && ok {
				files[1] = c.WebDir + "/templates/hub.html"
				tmpl, err := template.ParseFiles(files...)
				if err = template.Must(tmpl, err).Execute(w, session.Values["role"]); err != nil {
					log.Println(err)
					http.Error(w, err.Error(), http.StatusInternalServerError)
					return
				}
			} else {
				files[1] = c.WebDir + "/templates/login.html"
				tmpl, err := template.ParseFiles(files...)
				if err = template.Must(tmpl, err).Execute(w, nil); err != nil {
					log.Println(err)
					http.Error(w, err.Error(), http.StatusInternalServerError)
					return
				}
			}
		}
	}
}

func Login(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		userName := r.PostFormValue("username")
		password := r.PostFormValue("password")

		id := db.GetID(userName)
		if id == 0 {
			http.Error(w, fmt.Sprintf("no such user: %v", userName), http.StatusBadRequest)
			return
		}

		if err := db.CheckPassword(id, password); err != nil {
			log.Println(err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		user, err := db.GetUser(id)
		if err != nil {
			log.Println(err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		if err := saveSession(w, r, s, user); err != nil {
			log.Println(err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
		if err = template.Must(tmpl, err).ExecuteTemplate(w, "page-content", user.Role); err != nil {
			log.Println(err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}
}

func Logout(c *b.Config, s *b.CookieStore) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		session, err := getSession(w, r, c, s)
		if err != nil {
			log.Println(err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		session.Options.MaxAge = -1
		if err = session.Save(r, w); err != nil {
			log.Println(err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		tmpl, err := template.ParseFiles(c.WebDir + "/templates/login.html")
		if err = template.Must(tmpl, err).ExecuteTemplate(w, "page-content", nil); err != nil {
			log.Println(err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}
}

func ShowHub(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		session, err := getSession(w, r, c, s)
		if err != nil {
			log.Println(err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		session.Values["article"] = nil
		if err = session.Save(r, w); err != nil {
			log.Println(err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
		if err = template.Must(tmpl, err).ExecuteTemplate(w, "page-content", session.Values["role"].(int)); err != nil {
			log.Println(err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}
}