package main

import (
	"bufio"
	"os"
	"fmt"
	"io/ioutil"
	"net/http"
	"path/filepath"
	"regexp"
	"strings"
	"strconv"
)

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

type Recipe struct {
	Id	string
	Title	string
	Text	string
	Html	string
}

func titleFromMd(md string) string {
	scanner := bufio.NewScanner(strings.NewReader(md))
	for scanner.Scan() {
		line := scanner.Text()
		cut, found := strings.CutPrefix(line, "# ")
		if (found) {
			return cut
		}
	}
	return "no title detected"
}

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

	entries, err := os.ReadDir("data/storage/recipes")
	if err != nil {
		http.Error(w, "Could not list recipes!", 500)
		return
	}

	recipes := make([]Recipe, 0)

	for _,v := range entries {
		if v.IsDir() == false {
			continue
		}

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

		textpath := fmt.Sprintf("data/storage/recipes/%s/text", v.Name())
		data, _ := ioutil.ReadFile(textpath)

		recipes = append(recipes, Recipe{
			v.Name(),
			titleFromMd(string(data)),
			string(data),
			"",
		})
	}

	ServeTemplate(w, "index.html", recipes)
}

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]

	textpath := fmt.Sprintf("data/storage/recipes/%s/text", idStr)
	data, _ := ioutil.ReadFile(textpath)

	recipe := Recipe{
		idStr,
		titleFromMd(string(data)),
		string(data),
		"",
	}

	titleRegex := regexp.MustCompile(`\# .*`)
	recipe.Text = titleRegex.ReplaceAllString(recipe.Text, "")

	recipe.Html = fmt.Sprintf("<pre>%s</pre>", recipe.Text)

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

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]

	textpath := fmt.Sprintf("data/storage/recipes/%s/text", idStr)
	data, _ := ioutil.ReadFile(textpath)

	recipe := Recipe{
		idStr,
		"",
		string(data),
		"",
	}

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

func recipeEditPost(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
	}
	idStr := ids[0]

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

	buffer, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, "Could not read request body.", 400)
		return
	}

	textpath := fmt.Sprintf("data/storage/recipes/%s/text", idStr)
	err = ioutil.WriteFile(textpath, buffer, 0644)
	if err != nil {
		http.Error(w, "Could not save new text for recipe.", 500)
	}
}

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
	}

	recipe := Recipe{ids[0], "", "", ""}

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

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 := fmt.Sprintf("data/storage/recipes/%s", 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) {

	entries, err := os.ReadDir("data/storage/recipes")
	if err != nil {
		http.Error(w, "Could not get list of existing recipes!", 500)
		return
	}

	var biggest int = -1

	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

	recipedir := fmt.Sprintf("data/storage/recipes/%d", newId)
	err = os.Mkdir(recipedir, 0755)
	if err != nil {
		http.Error(w, "Could not create new recipe!", 500)
		return
	}

	textpath := fmt.Sprintf("data/storage/recipes/%d/text", newId)
	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.Http.Static, filename)
	http.ServeFile(w, r, path)
}