package main

import (
	"html/template"
	"io"
	"strconv"
	"time"

	"github.com/labstack/echo/v4"
	"github.com/labstack/echo/v4/middleware"
)

type Templates struct {
	templates *template.Template
}

func (t *Templates) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
	return t.templates.ExecuteTemplate(w, name, data)
}

func newTemplate() *Templates {
	return &Templates{
		templates: template.Must(template.ParseGlob("views/*.html")),
	}
}

var id = 0

type Entry struct {
	Name    string
	Message string
	Id      int
}

func newEntry(name, message string) Entry {
	id++
	return Entry{
		Name:    name,
		Message: message,
		Id:      id,
	}
}

type Entries = []Entry

type Data struct {
	Entries Entries
}

func (d *Data) indexOf(id int) int {
	for i, entry := range d.Entries {
		if entry.Id == id {
			return i
		}
	}
	return -1
}

func newData() Data {
	return Data{
		Entries: []Entry{
			newEntry("Asshole", "Hey, this is SHIT!"),
			newEntry("Not Bd", "Fist my ginger pubes, faghole"),
		},
	}
}

type FormData struct {
	Values map[string]string
	Errors map[string]string
}

func newFormData() FormData {
	return FormData{
		Values: make(map[string]string),
		Errors: make(map[string]string),
	}
}

type Page struct {
	Data Data
	Form FormData
}

func newPage() Page {
	return Page{
		Data: newData(),
		Form: newFormData(),
	}
}

func main() {

	e := echo.New()
	e.Use(middleware.Logger())

	page := newPage()
	e.Renderer = newTemplate()

	e.Static("/css", "css")
	e.Static("/images", "images")

	e.GET("/", func(c echo.Context) error {
		return c.Render(200, "index", page)
	})

	e.POST("/guestbook", func(c echo.Context) error {
		name := c.FormValue("name")
		message := c.FormValue("message")

		entry := newEntry(name, message)
		page.Data.Entries = append([]Entry{entry}, page.Data.Entries...)

		c.Render(200, "form", newFormData())
		return c.Render(200, "oob-entry", entry)
	})

	e.DELETE("/guestbook/:id", func(c echo.Context) error {

		// TODO: remove this to make server faster LOL
		time.Sleep(1 * time.Second)

		idStr := c.Param("id")
		id, err := strconv.Atoi(idStr)
		if err != nil {
			return c.String(400, "Invalid ID")
		}

		i := page.Data.indexOf(id)
		if i == -1 {
			return c.String(404, "Entry not found")
		}

		page.Data.Entries = append(page.Data.Entries[:i], page.Data.Entries[i+1:]...)
		return c.NoContent(200)

	})

	e.Logger.Fatal(e.Start(":1337"))
}
