//
// Created by vfs on 02.05.2024.
//

package main

import "fmt"
import "log"
import "net/http"
import "html/template"
//import "strconv"
import "strings"
import "unicode/utf8"
import "errors"
import "runtime/debug"

type templatedata struct {
	AuthenticatedUser int32
	FormErrors map[string]string
	Account Account
}

func favicon(w http.ResponseWriter, r *http.Request) {
	http.ServeFile(w, r, "favicon.ico")
}

func authenticated_user(r *http.Request) int32 {
	session, err := store.Get(r, "id")
	if err != nil {
		fmt.Println(err)
		return 0
	}

	id, ok := session.Values["id"].(int32)
	if !ok {
		trace := fmt.Sprintf("%s\n%s",  errors.New("type assertion to int32 failed").Error(), debug.Stack())
		_ = trace
		//log.Println(trace)
		return 0
	}

	return id
}

func home(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path != "/" {
		http.NotFound(w, r);
		return
	}

	id := authenticated_user(r)
	account, err := users.Get_account(id)

	text, err := template.ParseFiles("ui/base.html", "ui/index.html")
	if err != nil {
		http.Error(w, "Internal Server Error", 500)
		log.Fatal(err)
	}

	switch r.Method {
		case http.MethodGet:

			err := text.Execute(w, templatedata{AuthenticatedUser: id, Account: account})
			if err != nil {
				log.Fatal(err)
				http.Error(w, "Internal Server Error", 500)
			}

		case http.MethodPost:
			err := text.Execute(w, templatedata{})
			if err != nil {
				log.Fatal(err)
				http.Error(w, "Internal Server Error", 500)
			}
	}
}

func login(w http.ResponseWriter, r *http.Request) {
	text, err := template.ParseFiles("ui/base.html", "ui/login.html")
	if err != nil {
		http.Error(w, "Internal Server Error", 500)
		log.Fatal(err)
	}

	switch r.Method {
		case http.MethodGet:

			err := text.Execute(w, templatedata{})
			if err != nil {
				log.Fatal(err)
				http.Error(w, "Internal Server Error", 500)
			}

		case http.MethodPost:
			session, _ := store.Get(r, "id");
			password := r.FormValue("password")
			username := r.FormValue("username")
			errors := make(map[string]string)

			if strings.TrimSpace(username) == "" {
				errors["username"] = "This field cannot be blank"
			} else if utf8.RuneCountInString(username) > 20 {
				errors["username"] = "This field is too long (the maximum is 20 characters)"
			}

			if strings.TrimSpace(password) == "" {
				errors["password"] = "This field cannot be blank"
			} else if utf8.RuneCountInString(password) < 8 {
				errors["password"] = "This field is too short (the minimum is 8 characters)"
			}

			if len(errors) > 0 {

				err := text.Execute(w, templatedata{AuthenticatedUser: authenticated_user(r), FormErrors: errors})
				if err != nil {
					log.Fatal(err)
					http.Error(w, "Internal Server Error", 500)
				}

				return
			}

			id, err := users.Authenticate(username, password)
			if err == ErrInvalidCredentials {
				errors["generic"] = "Email or Password is incorrect"
				err := text.Execute(w, templatedata{AuthenticatedUser: authenticated_user(r), FormErrors: errors})
				if err != nil {
					log.Fatal(err)
					http.Error(w, "Internal Server Error", 500)
				}

				return
			}

			if id > 0 {
				session.Values["id"] = id
				fmt.Println("Logged in with id:", id)
				session.Save(r, w)
				http.Redirect(w, r, "/account", http.StatusSeeOther)
			}
	}
}

func logout(w http.ResponseWriter, r *http.Request) {
	text, err := template.ParseFiles("ui/base.html", "ui/logout.html")
	if err != nil {
		http.Error(w, "Internal Server Error", 500)
		log.Fatal(err)
	}

	id := authenticated_user(r)
	account, err := users.Get_account(id)

	switch r.Method {
		case http.MethodGet:

			err := text.Execute(w, templatedata{AuthenticatedUser: id, Account: account})
			if err != nil {
				log.Fatal(err)
				http.Error(w, "Internal Server Error", 500)
			}

		case http.MethodPost:
			session, _ := store.Get(r, "id");

			session.Values["id"] = 0;
			session.Save(r, w)
			http.Redirect(w, r, "/", http.StatusSeeOther)
	}
}

func register(w http.ResponseWriter, r *http.Request) {
	text, err := template.ParseFiles("ui/base.html", "ui/register.html")
	if err != nil {
		http.Error(w, "Internal Server Error", 500)
		log.Fatal(err)
	}

	id := authenticated_user(r)
	account, err := users.Get_account(id)

	switch r.Method {
		case http.MethodGet:

			err := text.Execute(w, templatedata{AuthenticatedUser: id, Account: account})
			if err != nil {
				log.Fatal(err)
				http.Error(w, "Internal Server Error", 500)
			}

		case http.MethodPost:
			account := Account{Username: r.FormValue("username"), Password: []byte(r.FormValue("password")), Firstname: r.FormValue("firstname"), Lastname: r.FormValue("lastname"), Email: r.FormValue("email")}

			errors := make(map[string]string)

			if strings.TrimSpace(account.Username) == "" {
				errors["username"] = "This field cannot be blank"
			} else if utf8.RuneCountInString(account.Username) > 20 {
				errors["username"] = "This field is too long (the maximum is 20 characters)"
			}

			if strings.TrimSpace(string(account.Password)) == "" {
				errors["password"] = "This field cannot be blank"
			} else if utf8.RuneCountInString(string(account.Password)) < 8 {
				errors["password"] = "This field is too short (the minimum is 8 characters)"
			}

			if len(errors) > 0 {

				err := text.Execute(w, templatedata{AuthenticatedUser: authenticated_user(r), FormErrors: errors})
				if err != nil {
					log.Fatal(err)
					http.Error(w, "Internal Server Error", 500)
				}
			}

			users.Insert(account.Username, string(account.Password), account.Firstname, account.Lastname, account.Email)
			http.Redirect(w, r, "/login", http.StatusSeeOther)
	}
}

func account(w http.ResponseWriter, r *http.Request) {
	//id, err := strconv.Atoi(r.URL.Query().Get("id"))
	//if err != nil || id < 1 {
	//	http.NotFound(w, r)
	//	return
	//}
	//account, err := users.Get_account(int32(id));
	//if err != nil {
	//	log.Fatal(err);
	//}

	id := authenticated_user(r)
	account, err := users.Get_account(id)

	text, err := template.ParseFiles("ui/base.html", "ui/account.html")

	if err != nil {
		http.Error(w, "Internal Server Error", 500)
		log.Fatal(err)
	}

	switch r.Method {
		case http.MethodGet:
			err := text.Execute(w, templatedata{AuthenticatedUser: id, Account: account})
			if err != nil {
				log.Fatal(err)
				http.Error(w, "Internal Server Error", 500)
			}
	}

	//case http.MethodPost:
	//	text.Execute(w, false)
	//	if err != nil {
	//		log.Fatal(err)
	//		http.Error(w, "Internal Server Error", 500)
	//	}
}

func deleteaccount(w http.ResponseWriter, r *http.Request) {

	id := authenticated_user(r)

	switch r.Method {
		case http.MethodPost:
			fmt.Println("Deleting account with id ", id)
			users.Delete(id)

			session, _ := store.Get(r, "id");

			session.Values["id"] = 0;
			session.Save(r, w)

			http.Redirect(w, r, "/", http.StatusSeeOther)
	}
}
