package editor

import (
	"bytes"
	"errors"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"text/template"

	"github.com/hacdias/caddy-hugo/frontmatter"
	"github.com/hacdias/caddy-hugo/utils"
	"github.com/spf13/hugo/parser"
)

type editor struct {
	Name        string
	Class       string
	Extension   string
	Content     string
	FrontMatter interface{}
}

// Execute sth
func Execute(w http.ResponseWriter, r *http.Request) (int, error) {
	filename := strings.Replace(r.URL.Path, "/admin/edit/", "", 1)

	if r.Method == "POST" {
		// TODO: review post saving
		/*
			// Get the JSON information sent using a buffer
			rawBuffer := new(bytes.Buffer)
			rawBuffer.ReadFrom(r.Body)

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

			// The main content of the file
			mainContent := rawFile["content"].(string)
			mainContent = "\n\n" + strings.TrimSpace(mainContent)

			// Removes the main content from the rest of the frontmatter
			delete(rawFile, "content")

			// Converts the frontmatter in JSON
			jsonFrontmatter, err := json.Marshal(rawFile)

			if err != nil {
				log.Print(err)
				return 500, err
			}

			// Indents the json
			frontMatterBuffer := new(bytes.Buffer)
			json.Indent(frontMatterBuffer, jsonFrontmatter, "", "  ")

			// Generates the final file
			file := new(bytes.Buffer)
			file.Write(frontMatterBuffer.Bytes())
			file.Write([]byte(mainContent))

			err = ioutil.WriteFile(filename, file.Bytes(), 0666)

			if err != nil {
				log.Print(err)
				return 500, err
			}

			w.Header().Set("Content-Type", "application/json")
			w.Write([]byte("{}")) */
	} else {
		// Check if the file format is supported. If not, send a "Not Acceptable"
		// header and an error
		if !CanBeEdited(filename) {
			return 406, errors.New("File format not supported.")
		}

		// Check if the file exists. If it doesn't, send a "Not Found" message
		if _, err := os.Stat(filename); os.IsNotExist(err) {
			log.Print(err)
			return 404, nil
		}

		// Open the file and check if there was some error while opening
		file, err := ioutil.ReadFile(filename)
		if err != nil {
			log.Print(err)
			return 500, err
		}

		// Create a new editor variable and set the extension
		page := new(editor)
		page.Extension = strings.TrimPrefix(filepath.Ext(filename), ".")
		page.Name = filename

		// Handle the content depending on the file extension
		switch page.Extension {
		case "markdown", "md":
			if hasFrontMatterRune(file) {
				// Starts a new buffer and parses the file using Hugo's functions
				buffer := bytes.NewBuffer(file)
				file, err := parser.ReadFrom(buffer)
				if err != nil {
					log.Print(err)
					return 500, err
				}

				// Parses the page content and the frontmatter
				page.Content = strings.TrimSpace(string(file.Content()))
				page.FrontMatter, err = frontmatter.Pretty(file.FrontMatter())
				page.Class = "full"
			} else {
				// The editor will handle only content
				page.Class = "content-only"
				page.Content = string(file)
			}
		case "json", "toml", "yaml":
			// Defines the class and declares an error
			page.Class = "frontmatter-only"
			var err error

			// Checks if the file already has the frontmatter rune and parses it
			if hasFrontMatterRune(file) {
				page.FrontMatter, err = frontmatter.Pretty(file)
			} else {
				page.FrontMatter, err = frontmatter.Pretty(appendFrontMatterRune(file, page.Extension))
			}

			// Check if there were any errors
			if err != nil {
				log.Print(err)
				return 500, err
			}
		default:
			// The editor will handle only content
			page.Class = "content-only"
			page.Content = string(file)
		}

		// Create the functions map, then the template, check for erros and
		// execute the template if there aren't errors
		functions := template.FuncMap{"splitCapitalize": utils.SplitCapitalize}
		tpl, err := utils.GetTemplate(r, functions, "editor", "frontmatter")
		if err != nil {
			log.Print(err)
			return 500, err
		}
		tpl.Execute(w, page)
	}

	return 200, nil
}

// CanBeEdited checks if a file has a supported extension
func CanBeEdited(filename string) bool {
	extensions := [...]string{".markdown", ".md",
		".json", ".toml", ".yaml",
		".css", ".sass", ".scss",
		".js",
		".html",
	}

	for _, extension := range extensions {
		if strings.HasSuffix(filename, extension) {
			return true
		}
	}

	return false
}

func hasFrontMatterRune(file []byte) bool {
	return strings.HasPrefix(string(file), "---") ||
		strings.HasPrefix(string(file), "+++") ||
		strings.HasPrefix(string(file), "{")
}

func appendFrontMatterRune(frontmatter []byte, language string) []byte {
	switch language {
	case "yaml":
		return []byte("---\n" + string(frontmatter) + "\n---")
	case "toml":
		return []byte("+++\n" + string(frontmatter) + "\n+++")
	case "json":
		return frontmatter
	}

	return frontmatter
}