package hugo

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"reflect"
	"strings"

	"github.com/hacdias/caddy-filemanager"
	"github.com/hacdias/caddy-filemanager/config"
	"github.com/hacdias/caddy-filemanager/directory"
	"github.com/hacdias/caddy-filemanager/frontmatter"
	"github.com/hacdias/caddy-hugo/utils/commands"
	"github.com/mholt/caddy"
	"github.com/mholt/caddy/caddyhttp/httpserver"
)

// AssetsURL is the base url for the assets
const AssetsURL = "/_hugointernal"

func init() {
	caddy.RegisterPlugin("hugo", caddy.Plugin{
		ServerType: "http",
		Action:     setup,
	})
}

// Setup is the init function of Caddy plugins and it configures the whole
// middleware thing.
func setup(c *caddy.Controller) error {
	cnf := httpserver.GetConfig(c)
	conf, _ := parse(c, cnf.Root)

	format := "toml"

	// Checks if there is an Hugo website in the path that is provided.
	// If not, a new website will be created.
	create := true

	if _, err := os.Stat(conf.Root + "config.yaml"); err == nil {
		format = "yaml"
		create = false
	}

	if _, err := os.Stat(conf.Root + "config.json"); err == nil {
		format = "json"
		create = false
	}

	if _, err := os.Stat(conf.Root + "config.toml"); err == nil {
		format = "toml"
		create = false
	}

	if create {
		err := commands.Run(conf.Hugo, []string{"new", "site", conf.Root, "--force"}, ".")
		if err != nil {
			log.Fatal(err)
		}
	}

	// Get Default FrontMatter
	bytes, err := ioutil.ReadFile(filepath.Clean(conf.Root + "/config." + format))

	if err != nil {
		log.Println(err)
		fmt.Printf("Can't get the default frontmatter from the configuration. %s will be used.\n", format)
	} else {
		bytes = directory.AppendFrontMatterRune(bytes, format)
		f, err := frontmatter.Unmarshal(bytes)

		if err != nil {
			log.Println(err)
			fmt.Printf("Can't get the default frontmatter from the configuration. %s will be used.\n", format)
		} else {
			kind := reflect.TypeOf(f)

			if kind == reflect.TypeOf(map[interface{}]interface{}{}) {
				if val, ok := f.(map[interface{}]interface{})["metaDataFormat"]; ok {
					format = val.(string)
				}

			} else {
				if val, ok := f.(map[string]interface{})["metaDataFormat"]; ok {
					format = val.(string)
				}
			}
		}
	}

	// Generates the Hugo website for the first time the plugin is activated.
	go RunHugo(conf, true)

	mid := func(next httpserver.Handler) httpserver.Handler {
		return &Hugo{
			Next:   next,
			Config: conf,
			FileManager: &filemanager.FileManager{
				Next: next,
				Configs: []config.Config{
					config.Config{
						HugoEnabled: true,
						PathScope:   conf.Root,
						FrontMatter: format,
						Root:        http.Dir(conf.Root),
						BaseURL:     conf.BaseURL,
						AbsoluteURL: strings.Replace(cnf.Addr.Path+"/"+conf.BaseURL, "//", "/", -1),
						AddrPath:    strings.TrimSuffix(cnf.Addr.Path, "/"),
						StyleSheet:  conf.Styles,
					},
				},
			},
		}
	}

	cnf.AddMiddleware(mid)
	return nil
}

// Config is a configuration for managing a particular hugo website.
type Config struct {
	Public      string   // Public content path
	Root        string   // Hugo files path
	Hugo        string   // Hugo executable location
	Styles      string   // Admin styles path
	Args        []string // Hugo arguments
	BaseURL     string   // BaseURL of admin interface
	FileManager *filemanager.FileManager
}

// Parse parses the configuration set by the user so it can be
// used by the middleware
func parse(c *caddy.Controller, root string) (*Config, error) {
	conf := &Config{
		Public:  strings.Replace(root, "./", "", -1),
		BaseURL: "/admin",
		Root:    "./",
	}

	conf.Hugo = getPath()
	for c.Next() {
		args := c.RemainingArgs()

		switch len(args) {
		case 1:
			conf.Root = args[0]
			conf.Root = strings.TrimSuffix(conf.Root, "/")
			conf.Root += "/"
		}

		for c.NextBlock() {
			switch c.Val() {
			case "styles":
				if !c.NextArg() {
					return conf, c.ArgErr()
				}
				tplBytes, err := ioutil.ReadFile(c.Val())
				if err != nil {
					return conf, err
				}
				conf.Styles = string(tplBytes)
			case "admin":
				if !c.NextArg() {
					return conf, c.ArgErr()
				}
				conf.BaseURL = c.Val()
				conf.BaseURL = strings.TrimPrefix(conf.BaseURL, "/")
				conf.BaseURL = "/" + conf.BaseURL
			default:
				key := "--" + c.Val()
				value := "true"

				if c.NextArg() {
					value = c.Val()
				}

				conf.Args = append(conf.Args, key+"="+value)
			}
		}
	}

	return conf, nil
}