package browse

import (
	"bytes"
	"encoding/json"
	"errors"
	"net/http"
	"os"
	"strings"
	"text/template"

	"github.com/hacdias/caddy-hugo/config"
	"github.com/hacdias/caddy-hugo/utils"
	"github.com/mholt/caddy/middleware"
	"github.com/mholt/caddy/middleware/browse"
)

// ServeHTTP is used to serve the content of Browse page
// using Browse middleware from Caddy
func ServeHTTP(w http.ResponseWriter, r *http.Request, c *config.Config) (int, error) {
	// Removes the page main path from the URL
	r.URL.Path = strings.Replace(r.URL.Path, "/admin/browse", "", 1)

	switch r.Method {
	case "DELETE":
		return delete(w, r)
	case "POST":
		return post(w, r)
	case "GET":
		return get(w, r, c)
	default:
		return 400, nil
	}
}

func delete(w http.ResponseWriter, r *http.Request) (int, error) {
	// Remove both beginning and trailing slashes
	r.URL.Path = strings.TrimPrefix(r.URL.Path, "/")
	r.URL.Path = strings.TrimSuffix(r.URL.Path, "/")

	// Check if the file or directory exists
	if stat, err := os.Stat(r.URL.Path); err == nil {
		var err error
		// If it's dir, remove all of the content inside
		if stat.IsDir() {
			err = os.RemoveAll(r.URL.Path)
		} else {
			err = os.Remove(r.URL.Path)
		}

		// Check for errors
		if err != nil {
			w.Write([]byte(err.Error()))
			return 500, err
		}
	} else {
		return 404, nil
	}

	w.Header().Set("Content-Type", "application/json")
	w.Write([]byte("{}"))
	return 200, nil
}

func post(w http.ResponseWriter, r *http.Request) (int, error) {
	// Remove both beginning  slashes
	r.URL.Path = strings.TrimPrefix(r.URL.Path, "/")
	// Get the JSON information sent using a buffer
	buffer := new(bytes.Buffer)
	buffer.ReadFrom(r.Body)

	// Creates the raw file "map" using the JSON
	var info map[string]interface{}
	json.Unmarshal(buffer.Bytes(), &info)

	// Check if filename and archetype are specified in
	// the request
	if _, ok := info["filename"]; !ok {
		return 400, errors.New("Filename not specified.")
	}

	if _, ok := info["archetype"]; !ok {
		return 400, errors.New("Archtype not specified.")
	}

	// Sanitize the file name path
	filename := info["filename"].(string)
	filename = strings.TrimPrefix(filename, "/")
	filename = strings.TrimSuffix(filename, "/")
	filename = r.URL.Path + filename

	// Check if the archetype is defined
	if info["archetype"] != "" {
		// Sanitize the archetype path
		archetype := info["archetype"].(string)
		archetype = strings.Replace(archetype, "/archetypes", "", 1)
		archetype = strings.Replace(archetype, "archetypes", "", 1)
		archetype = strings.TrimPrefix(archetype, "/")
		archetype = strings.TrimSuffix(archetype, "/")
		archetype = "archetypes/" + archetype

		// Check if the archetype ending with .markdown exists
		if _, err := os.Stat(archetype + ".markdown"); err == nil {
			err = utils.CopyFile(archetype+".markdown", filename)

			if err != nil {
				w.Write([]byte(err.Error()))
				return 500, err
			}

			w.Header().Set("Location", "/admin/edit/"+filename)
			w.Header().Set("Content-Type", "application/json")
			w.Write([]byte("{}"))
			return 201, nil
		}

		// Check if the archetype ending with .md exists
		if _, err := os.Stat(archetype + ".md"); err == nil {
			err = utils.CopyFile(archetype+".md", filename)

			if err != nil {
				w.Write([]byte(err.Error()))
				return 500, err
			}

			w.Header().Set("Location", "/admin/edit/"+filename)
			w.Header().Set("Content-Type", "application/json")
			w.Write([]byte("{}"))
			return 201, nil
		}
	}

	wf, err := os.Create(filename)

	if err != nil {
		w.Write([]byte(err.Error()))
		return 500, err
	}

	defer wf.Close()

	w.Header().Set("Location", "/admin/edit/"+filename)
	w.Header().Set("Content-Type", "application/json")
	w.Write([]byte("{}"))
	return 200, nil
}

func get(w http.ResponseWriter, r *http.Request, c *config.Config) (int, error) {
	functions := template.FuncMap{
		"CanBeEdited": utils.CanBeEdited,
		"Defined":     utils.Defined,
	}

	tpl, err := utils.GetTemplate(r, functions, "browse")

	if err != nil {
		w.Write([]byte(err.Error()))
		return 500, err
	}

	b := browse.Browse{
		Next: middleware.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
			return 404, nil
		}),
		Root: "./",
		Configs: []browse.Config{
			browse.Config{
				PathScope: "/",
				Variables: c,
				Template:  tpl,
			},
		},
		IgnoreIndexes: true,
	}

	return b.ServeHTTP(w, r)
}