package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"sort"
	"strconv"
)

const (
	VALID_ID_REGEX = `^[0-9]+$`
)

func indexGet(w http.ResponseWriter, r *http.Request) {
	list := getRecipeList()
	sort.Sort(list)
	ServeTemplate(w, "index.html", list)
}

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

	ids := r.URL.Query()["id"]
	if len(ids) != 1 {
		msg := fmt.Sprintf("Exactly 1 'id' URL parameter expected but %d provided.", len(ids))
		http.Error(w, msg, 400)
		return
	}
	idStr := ids[0]

	rec, err := getRecipe(idStr)
	if err != nil {
		http.Error(w, "Could not get recipe.", 400)
		return
	}

	data := struct {
		Id     string
		Recipe recipe
	}{idStr, rec}

	ServeTemplate(w, "recipe.html", data)
}

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

	ids := r.URL.Query()["id"]
	if len(ids) != 1 {
		msg := fmt.Sprintf("Exactly 1 'id' URL parameter expected but %d provided.", len(ids))
		http.Error(w, msg, 400)
		return
	}
	idStr := ids[0]

	text, err := getRecipeText(idStr)
	if err != nil {
		http.Error(w, "Could not get recipe.", 400)
		return
	}

	rec, err := getRecipe(idStr)
	if err != nil {
		http.Error(w, "Could not get recipe.", 400)
		return
	}

	recipe := struct {
		Id    string
		Title string
		Text  string
	}{idStr, rec.Title, string(text)}

	ServeTemplate(w, "recipe_edit.html", recipe)
}

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

	r.ParseForm()

	for _, v := range []string{"id", "text"} {
		if len(r.Form[v]) != 1 {
			http.Error(w, "Exactly 1 '"+v+"' form parameter expected.", 400)
			return
		}
	}

	idStr := r.Form["id"][0]
	idRegex := regexp.MustCompile(VALID_ID_REGEX)
	if !(idRegex.MatchString(idStr)) {
		http.Error(w, "Bad 'id' URL parameter.", 400)
		return
	}

	buffer := []byte(r.Form["text"][0])
	err := json.Unmarshal(buffer, &recipe{})
	if err != nil {
		http.Error(w, "Text input could not be parsed to recipe.", 400)
		return
	}

	textpath := filepath.Join(config.Data, "recipes", idStr, "text")
	err = ioutil.WriteFile(textpath, buffer, 0644)
	if err != nil {
		http.Error(w, "Could not save new text for recipe.", 500)
	}

	http.Redirect(w, r, "/recipe?id="+idStr, 303)
}

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

	ids := r.URL.Query()["id"]
	if len(ids) != 1 {
		http.Error(w, "Exactly 1 'id' URL parameter expected.", 400)
		return
	}

	ServeTemplate(w, "recipe_confirm_deletion.html", struct{ Id string }{ids[0]})
}

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

	ids := r.URL.Query()["id"]
	if len(ids) != 1 {
		http.Error(w, "Exactly 1 'id' URL parameter expected.", 400)
		return
	}

	recipedir := filepath.Join(config.Data, "recipes", ids[0])
	err := os.RemoveAll(recipedir)
	if err != nil {
		http.Error(w, "Could not delete recipe.", 500)
		return
	}

	http.Redirect(w, r, "/index.html", 303)
}

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

	var biggest int = -1

	entries, _ := os.ReadDir(filepath.Join(config.Data, "recipes"))
	for _, v := range entries {
		if v.IsDir() == false {
			continue
		}

		number, err := strconv.Atoi(v.Name())
		if err != nil {
			continue
		}

		if number > biggest {
			biggest = number
		}
	}

	newId := biggest + 1
	newIdStr := strconv.Itoa(newId)

	recipedir := filepath.Join(config.Data, "recipes", newIdStr)
	err := os.Mkdir(recipedir, 0755)
	if err != nil {
		http.Error(w, "Could not create new recipe!", 500)
		return
	}

	textpath := filepath.Join(config.Data, "recipes", newIdStr, "text")
	err = os.WriteFile(textpath, make([]byte, 0), 0644)
	if err != nil {
		http.Error(w, "Could not create new recipe!", 500)
		return
	}

	redirect := fmt.Sprintf("/recipe/edit?id=%d", newId)
	http.Redirect(w, r, redirect, 303)
}

func staticGet(w http.ResponseWriter, r *http.Request, filename string) {

	path := filepath.Join(config.Static, filename)
	http.ServeFile(w, r, path)
}