package webserver

import (
	"fmt"
	"log"
	"mime/multipart"
	"net/http"
	"strconv"
	"time"

	"git.tovijaeschke.xyz/tovi/personal_website/database"
	"git.tovijaeschke.xyz/tovi/personal_website/helper"
	"git.tovijaeschke.xyz/tovi/personal_website/variables"

	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
	"golang.org/x/crypto/bcrypt"
)

func CheckAuth(w http.ResponseWriter, r *http.Request) bool {
	var (
		session       *sessions.Session
		lastLoginUnix int64
		lastLogin     time.Time
		auth          bool
		exists        bool
		e             error
	)
	session, e = variables.CookieStore.Get(r, variables.CookieName)
	if e != nil {
		return false
	}

	auth, exists = session.Values["authenticated"].(bool)
	if !(auth && exists) {
		return false
	}

	lastLoginUnix, exists = session.Values["lastLogin"].(int64)
	if !exists {
		return false
	}
	lastLogin = time.Unix(lastLoginUnix, 0)
	lastLogin = lastLogin.Add(3 * time.Hour)

	if time.Now().After(lastLogin) {
		session.Values = make(map[interface{}]interface{})
		session.Values["authenticated"] = false
		session.AddFlash("Login Expired")
		e = session.Save(r, w)
		if e != nil {
			log.Println(e.Error())
		}

		return false
	}

	return true
}

func AdminView(w http.ResponseWriter, r *http.Request) {
	var (
		v          = make(map[string]interface{})
		urlParams  map[string]string
		page       string
		pageInt    int
		pageOffset int
		posts      []database.Post
		exists     bool
		e          error
	)

	if !CheckAuth(w, r) {
		http.Redirect(w, r, "/admin/login", 302)
		return
	}

	switch r.Method {
	case http.MethodGet:

		urlParams = mux.Vars(r)

		page, exists = urlParams["page"]
		if exists {
			pageInt, e = strconv.Atoi(page)
			if e != nil {
				log.Fatal("Url Parameter page cannot be converted to an int")
			}
		} else {
			pageInt = 0
		}

		pageOffset = pageInt * 10

		posts, e = database.GetPostsList(10, pageOffset)

		v["Posts"] = posts

		ServeTemplate(w, r, "html/admin/admin-index.gohtml", v)
		return
	}
}

func comparePasswords(hashedPwd, plainPwd string) bool {
	var (
		e error
	)
	e = bcrypt.CompareHashAndPassword(
		[]byte(hashedPwd),
		[]byte(plainPwd),
	)
	if e != nil {
		return false
	}

	return true
}

func AdminLogout(w http.ResponseWriter, r *http.Request) {
	var (
		session *sessions.Session
		e       error
	)

	if !CheckAuth(w, r) {
		http.Redirect(w, r, "/admin/login", 302)
		return
	}

	session, e = variables.CookieStore.Get(r, variables.CookieName)
	if e != nil {
		log.Println("Could not get session cookie")
		http.Error(w, "Error", http.StatusInternalServerError)
		return
	}

	session.Values["authenticated"] = false
	session.Values["lastLogin"] = nil
	session.Save(r, w)

	http.Redirect(w, r, "/admin/logout", 302)
	return
}

func AdminLogin(w http.ResponseWriter, r *http.Request) {
	var (
		session  *sessions.Session
		v        = make(map[string]interface{})
		flashes  []interface{}
		username string
		password string
		e        error
	)

	if CheckAuth(w, r) {
		http.Redirect(w, r, "/admin", 302)
		return
	}

	session, e = variables.CookieStore.Get(r, variables.CookieName)
	if e != nil {
		log.Println("Could not get session cookie")
		http.Error(w, "Error", http.StatusInternalServerError)
		return
	}

	switch r.Method {
	case http.MethodGet:
		flashes = session.Flashes()
		e = session.Save(r, w)
		if e != nil {
			log.Println(e.Error())
			return
		}
		if len(flashes) > 0 {
			v["FlashMsg"] = flashes[0].(string)
		}

		ServeTemplate(w, r, "html/admin/admin-login.gohtml", v)
		return

	case http.MethodPost:
		e = r.ParseForm()
		if e != nil {
			log.Println(e.Error())
			http.Redirect(w, r, "/login", 302)
		}

		username = r.FormValue("username")
		password = r.FormValue("password")

		if username != variables.AdminPassword && !comparePasswords(variables.AdminPassword, password) {
			session.AddFlash("Invalid Username or Password")
			e = session.Save(r, w)
			if e != nil {
				log.Println(e.Error())
			}
			http.Redirect(w, r, "/admin/login", 302)
			return

		}

		session.Values["authenticated"] = true
		session.Values["lastLogin"] = time.Now().Unix()
		session.Save(r, w)

		http.Redirect(w, r, "/admin", 302)
		return
	}
}

func AdminNewPost(w http.ResponseWriter, r *http.Request) {
	var (
		session                     *sessions.Session
		v                           = make(map[string]interface{})
		flashes                     []interface{}
		title, subject, intro, body string
		bodyPath                    string
		mainFilePath                string = ""
		fileUpload                  []*multipart.FileHeader
		//otherImgs                   string
		e error
	)

	if !CheckAuth(w, r) {
		http.Redirect(w, r, "/admin/login", 302)
		return
	}

	session, e = variables.CookieStore.Get(r, variables.CookieName)
	if e != nil {
		log.Println("Could not get session cookie")
		http.Error(w, "Error", http.StatusInternalServerError)
		return
	}

	switch r.Method {
	case http.MethodGet:
		flashes = session.Flashes()
		e = session.Save(r, w)
		if e != nil {
			log.Println(e.Error())
			return
		}
		if len(flashes) > 0 {
			v["FlashMsg"] = flashes[0].(string)
		}

		ServeTemplate(w, r, "html/admin/admin-new-post.gohtml", v)
		return

	case http.MethodPost:

		title = r.FormValue("title")
		subject = r.FormValue("subject")
		intro = r.FormValue("intro")
		body = r.FormValue("body")

		bodyPath, e = helper.WriteBody(title, body)
		if e != nil {
			log.Fatal(e)
		}

		r.ParseMultipartForm(32 << 20) // 32MB is the default used by FormFile
		fileUpload = r.MultipartForm.File["img"]

		helper.UploadFiles(fileUpload)

		if len(fileUpload) == 1 {
			mainFilePath = fileUpload[0].Filename
		}

		fileUpload = r.MultipartForm.File["files"]

		helper.UploadFiles(fileUpload)

		database.CreatePost(
			database.Post{
				Title:     title,
				Subject:   subject,
				Intro:     intro,
				HtmlPath:  bodyPath,
				MainImage: mainFilePath,
			},
		)

		http.Redirect(w, r, "/admin", 302)
		return
	}
}

func AdminEditPost(w http.ResponseWriter, r *http.Request) {
	var (
		session                     *sessions.Session
		v                           = make(map[string]interface{})
		urlParams                   map[string]string
		flashes                     []interface{}
		title, subject, intro, body string
		bodyPath                    string
		post                        database.Post
		fileUpload                  []*multipart.FileHeader
		e                           error
	)

	if !CheckAuth(w, r) {
		http.Redirect(w, r, "/admin/login", 302)
		return
	}

	session, e = variables.CookieStore.Get(r, variables.CookieName)
	if e != nil {
		log.Println("Could not get session cookie")
		http.Error(w, "Error", http.StatusInternalServerError)
		return
	}

	switch r.Method {
	case http.MethodGet:
		flashes = session.Flashes()
		e = session.Save(r, w)
		if e != nil {
			log.Println(e.Error())
			return
		}
		if len(flashes) > 0 {
			v["FlashMsg"] = flashes[0].(string)
		}

		urlParams = mux.Vars(r)

		post, e = database.GetPostById(urlParams["id"])
		if e != nil {
			log.Fatal("Cannot get Post by id")
		}

		post.Body, e = helper.GetFileContents(post.HtmlPath)
		if e != nil {
			log.Fatal("Cannot read body file")
		}

		v["Post"] = post

		ServeTemplate(w, r, "html/admin/admin-update-post.gohtml", v)
		return

	case http.MethodPost:

		urlParams = mux.Vars(r)

		post, e = database.GetPostById(urlParams["id"])
		if e != nil {
			log.Fatal("Cannot get Post by id")
		}

		title = r.FormValue("title")
		subject = r.FormValue("subject")
		intro = r.FormValue("intro")
		body = r.FormValue("body")

		if title != post.Title {
			defer helper.DeleteOldPostFile(post.Title)
		}

		bodyPath, e = helper.WriteBody(title, body)
		if e != nil {
			log.Fatal(e)
		}

		r.ParseMultipartForm(32 << 20) // 32MB is the default used by FormFile
		fileUpload = r.MultipartForm.File["img"]

		helper.UploadFiles(fileUpload)

		if len(fileUpload) == 1 {
			post.MainImage = fileUpload[0].Filename
		}

		fileUpload = r.MultipartForm.File["files"]

		helper.UploadFiles(fileUpload)

		post.Title = title
		post.Subject = subject
		post.Intro = intro
		post.HtmlPath = bodyPath

		database.UpdatePost(
			post,
		)

		http.Redirect(w, r, fmt.Sprintf("/admin/post/%s/edit", post.ID), 302)
		return
	}
}
