package web

import (
	"bytes"
	"fmt"
	"regexp"
	"log"
	"io/ioutil"
	"net/http"
	"path/filepath"

	"xengineering.eu/ceres/utils"

	"github.com/yuin/goldmark"
)

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

func static(filename string, staticRoot string) func(http.ResponseWriter, *http.Request) {

	return func(w http.ResponseWriter, r *http.Request) {
		path := filepath.Join(staticRoot, filename)
		log.Printf("Trying to serve: %s\n", path)
		http.ServeFile(w, r, path)
	}
}

func index(db *utils.Database, templateRoot string) func(http.ResponseWriter, *http.Request) {

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

		// get data from database
		cmd := "SELECT id,title FROM recipes ORDER BY title;"
		log.Printf("Query: %s", cmd)
		rows, err := db.Backend.Query(cmd)
		if err != nil {
			utils.Err(w, 1)
			return
		}
		defer rows.Close()

		// prepare data store
		type Element struct {
			Id string
			Title string
		}
		elements := make([]Element, 0)

		// scan database rows to data store
		for rows.Next() {
			var element Element
			err := rows.Scan(&element.Id, &element.Title)
			if err != nil {
				utils.Err(w, 2)
				return
			} else {
				elements = append(elements, element)
			}
		}

		// render and return template
		path := filepath.Join(templateRoot, "index.html")
		utils.ServeTemplate(w, "index", path, elements)
	}
}

func recipe(db *utils.Database, templateRoot string) func(http.ResponseWriter, *http.Request) {

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

		// get id from URL parameters
		ids := r.URL.Query()["id"]
		if len(ids) != 1 {
			utils.Err(w, 3, len(ids))
			return
		}
		idStr := ids[0]

		// validate id
		idRegex := regexp.MustCompile(VALID_ID_REGEX)
		if !(idRegex.MatchString(idStr)) {
			utils.Err(w, 4, idStr, VALID_ID_REGEX)
			return
		}

		if r.Method == "GET" {

			// get data from database
			cmd := fmt.Sprintf("SELECT title,upstream_url,description_markdown FROM recipes WHERE (id='%s');", idStr)
			log.Printf("Query: %s", cmd)
			rows, err := db.Backend.Query(cmd)
			if err != nil {
				utils.Err(w, 5, err)
				return
			}
			defer rows.Close()

			// prepare data store
			type Element struct {
				Id string
				Title string
				UpstreamUrl string
				DescriptionMarkdown string
				RenderedDescriptionMarkdown string
			}
			elements := make([]Element, 0)

			// scan database rows to data store
			for rows.Next() {
				var element Element
				element.Id = idStr
				err := rows.Scan(&element.Title, &element.UpstreamUrl, &element.DescriptionMarkdown)
				if err != nil {
					utils.Err(w, 2)
					return
				} else {
					elements = append(elements, element)
				}
			}

			// check result
			if len(elements) != 1 {
				utils.Err(w, 6, len(elements))
				return
			}

			// render markdown
			var buf bytes.Buffer
			goldmark.Convert([]byte(elements[0].DescriptionMarkdown), &buf)
			elements[0].RenderedDescriptionMarkdown = buf.String()

			// render and return template
			path := filepath.Join(templateRoot, "recipe.html")
			utils.ServeTemplate(w, "recipe", path, elements[0])
		}

		if r.Method == "POST" {

			// read request body
			buffer,_ := ioutil.ReadAll(r.Body)  // FIXME error handling
			body := string(buffer)
			updateRecipe(db, body, idStr)

		}
	}
}

func updateRecipe(db *utils.Database, body string, idStr string) {

	// execute SQL UPDATE
	_,_ = db.Backend.Exec(`
		UPDATE
			recipes
		SET
			description_markdown=?
		WHERE
			(id=?);
		`,
		body, idStr,
	)  // FIXME error handling

	return
}

func image(storageRoot string) func(http.ResponseWriter, *http.Request) {

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

		// get ID
		ids := r.URL.Query()["id"]
		if len(ids) != 1 {
			utils.Err(w, 3, len(ids))
			return
		}
		idStr := ids[0]

		// validate ID
		idRegex := regexp.MustCompile(VALID_ID_REGEX)
		if !idRegex.MatchString(idStr) {
			utils.Err(w, 4, idStr, VALID_ID_REGEX)
			return
		}

		// serve image
		path := fmt.Sprintf("recipes/image/%s.jpg", idStr)
		utils.ServeStorage(w, r, storageRoot, path)
	}
}

func add_recipes(db *utils.Database, storageRoot string, staticRoot string) func(http.ResponseWriter, *http.Request) {

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

		if r.Method == "GET" {
			filename := "add.html"
			path := filepath.Join(staticRoot, filename)
			log.Printf("Trying to serve: %s", path)
			http.ServeFile(w, r, path)
			return
		}

		if r.Method == "POST" {
			url := r.FormValue("url")
			title := r.FormValue("title")

			cmd := fmt.Sprintf("INSERT INTO recipes (title,upstream_url) VALUES ('%s', '%s')", title, url)
			log.Println(cmd)
			res,err := db.Backend.Exec(cmd)
			if err != nil {
				utils.Err(w, 9, err)
				return
			}
			id,err := res.LastInsertId()
			if err != nil {
				utils.Err(w, 11, err)
				return
			} else {
				log.Println("Added custom recipe.")
				redirect := fmt.Sprintf("/recipe?id=%d", id)
				http.Redirect(w, r, redirect, 303)
				return
			}
		}
	}
}