package main

import (
	"flag"
	"fmt"
	"net/http"
	"os"
	"os/signal"
	"os/user"
	"path/filepath"
	"strings"
	"time"

	"github.com/kernelschmelze/inbox/handler"
	"github.com/kernelschmelze/inbox/handler/pushover"
	"github.com/kernelschmelze/inbox/store"
	"github.com/kernelschmelze/inbox/store/json"

	log "github.com/kernelschmelze/pkg/logger"
	"github.com/kernelschmelze/pkg/srv"

	"github.com/pelletier/go-toml"
)

const (
	maxFileSize = 1024 * 1024 * 5 // 5MB
)

type config struct {
	Listen    string
	Crt       string
	Key       string
	Path      string
	Pushover  pushover.Config
	JsonStore jsonstore.Config
}

func main() {

	path := flag.String("f", "./inbox.toml", "config file")

	flag.Parse()

	// read config from file

	config, err := readConfig(*path)
	if err != nil {
		log.Errorf("read config failed, err=%s", err)
	}

	var store store.Store

	// currently only a simple json store is supported

	if store == nil {

		if len(config.JsonStore.Path) == 0 {
			config.JsonStore.Path = "./data"
		}

		if config.JsonStore.Path, err = expandPath(config.JsonStore.Path); err != nil {
			log.Errorf("get data path failed, err=%s", err)
		} else {
			log.Infof("use data path '%s'", config.JsonStore.Path)
			store = jsonstore.New(config.JsonStore)
		}

	}

	// http handler

	handler := handler.New(maxFileSize, store)

	// pushover plugin

	if len(config.Pushover.User) != 0 && len(config.Pushover.App) != 0 {
		pushover := pushover.New(config.Pushover)
		handler.AddPlugin(pushover)
	}

	// run the server

	if len(config.Listen) == 0 {
		config.Listen = ":25478"
	}

	server := srv.New(onListen, onShutdown)

	err = server.Add(srv.Config{
		config.Listen,
		handler,
		config.Crt,
		config.Key,
	})

	if err != nil {
		log.Errorf("server '%s' failed, err=%s", config.Listen, err)
	}

	defer func() {
		server.Close()
		handler.Close()
		if store != nil {
			store.Close()
		}
	}()

	signalHandler()

}

func signalHandler() {

	var gracefulStop = make(chan os.Signal)
	signal.Notify(gracefulStop, os.Interrupt)

	select {
	case <-gracefulStop:
		fmt.Println("")
	}

	go func() {
		select {
		case <-time.After(10 * time.Second):
			fmt.Println("kill app")
			os.Exit(1)
		}
	}()

}

func onListen(addr string, crtFile string, keyFile string) {

	if len(crtFile) == 0 || len(keyFile) == 0 {
		log.Infof("listen on '%s'", addr)
	} else {
		log.Infof("listen on '%s', crt '%s', key '%s'", addr, crtFile, keyFile)
	}

}

func onShutdown(addr string, err error) {

	if err == nil {
		log.Infof("shutdown '%s'", addr)
		return
	}

	if err != http.ErrServerClosed {
		log.Errorf("shutdown '%s', err=%s", addr, err)
	} else {
		log.Infof("shutdown '%s', err=%s", addr, err)
	}

}

func readConfig(path string) (config, error) {

	var err error
	var tml *toml.Tree

	c := config{}

	if tml, err = toml.LoadFile(path); err == nil {
		err = tml.Unmarshal(&c)
	}

	return c, err
}

func expandPath(path string) (string, error) {

	if path == "" {
		return "", nil
	}

	if strings.HasPrefix(path, "~") {
		usr, err := user.Current()
		if err != nil {
			return "", err
		}
		path = strings.Replace(path, "~", usr.HomeDir, 1)
	}

	return filepath.Abs(path)

}