package main

import (
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"path"
	"time"

	"github.com/VictorAvelar/mollie-api-go/mollie"
	"gopkg.in/yaml.v2"

	"github.com/gin-gonic/gin"
	"github.com/jinzhu/gorm"
)

type Config struct {
	MollieToken       string `yaml:"mollie_token"`
	MollieTesting     bool   `yaml:"mollie_testing"`
	MollieWebhook     string `yaml:"mollie_webhook"`
	MollieRedirectURL string `yaml:"mollie_redirect_url"`
	MailServer        string `yaml:"mail_server"`
	MailPort          int    `yaml:"mail_port"`
	MailUser          string `yaml:"mail_user"`
	MailPass          string `yaml:"mail_pass"`
	FromAddr          string `yaml:"from_addr"`
	BCC               string `yaml:"bcc"`
}

var config Config

var mollieClient *mollie.Client

var ROOT_DIR string

func notFound(w http.ResponseWriter, r *http.Request) error {
	w.WriteHeader(http.StatusNotFound)

	t, err := template.ParseFiles(path.Join(ROOT_DIR, "templates/404.html"))

	if err != nil {
		return err
	}

	return t.Execute(w, nil)
}

func index(c *gin.Context) {
	events := getEvents("loveit")
	tomEvents := getEvents("tom_waits")

	c.HTML(http.StatusOK, "index.html", map[string]interface{}{
		"events":    events,
		"tomEvents": tomEvents,
		"nowYear":   time.Now().Format("2006"),
	})
}

func loveIt(c *gin.Context) {
	c.Redirect(http.StatusFound, "/")
}

func albumsHandler(c *gin.Context) {
	albums := getAlbums()

	c.HTML(http.StatusOK, "albums.html", map[string]interface{}{
		"nowYear": time.Now().Format("2006"),
		"albums":  albums,
	})
}

func handle404(c *gin.Context) {
	c.HTML(http.StatusOK, "404.html", map[string]interface{}{})
}

func songsHandler(c *gin.Context) {
	albums := getAlbums()
	albumTitle := c.Param("album")
	songTitle := c.Param("song")

	album, err := getAlbum(albums, albumTitle)

	if err != nil {
		c.HTML(http.StatusNotFound, "404.html", map[string]interface{}{})
		return
	}

	song, err := getSong(album, songTitle)

	if err != nil {
		c.HTML(http.StatusNotFound, "404.html", map[string]interface{}{})
		return
	}

	c.HTML(http.StatusOK, "song.html", map[string]interface{}{
		"nowYear":    time.Now().Format("2006"),
		"album":      album,
		"song":       song,
		"albumTitle": albumTitle,
	})
}

func createSocket(s string) net.Listener {
	os.Remove(s)

	log.Println("Creating UNIX socket:", s)
	sock, err := net.Listen("unix", s)
	if err != nil {
		panic(err)
	}

	err = os.Chmod(s, 0777)

	if err != nil {
		panic(err)
	}

	return sock
}

func getDB(c *gin.Context) *gorm.DB {
	item, _ := c.Get("db")
	db := item.(*gorm.DB)

	return db
}

func main() {
	if len(os.Args) > 1 {
		ROOT_DIR = os.Args[1]
	} else {
		ROOT_DIR = "/var/www/flipnew"
	}

	r := gin.New()

	r.SetFuncMap(template.FuncMap{
		"formatMoney": func(cents int) string {
			return fmt.Sprintf("€%.2f", float64(float64(cents)/100))
		},
	})

	log.Printf("Running from with app root dir: %s\n", ROOT_DIR)

	db := GetDB()
	defer db.Close()

	configPath := path.Join(ROOT_DIR, "data/config/config.yaml")
	configBytes, err := ioutil.ReadFile(configPath)

	if err != nil {
		panic(fmt.Sprintf("Can't open config file at %s; %v", configPath, err))
	}

	if err := yaml.Unmarshal([]byte(configBytes), &config); err != nil {
		panic(fmt.Sprintf("Can't parse config file at %s; %v", configPath, err))
	}

	if mollieClient, err = initMollie(); err != nil {
		panic(err)
	}

	r.LoadHTMLGlob(path.Join(ROOT_DIR, "templates/*.html"))

	r.Use(func(c *gin.Context) {
		c.Set("db", db)
		c.Next()
	})

	if k, v := os.LookupEnv("RUN_STATIC"); v && k == "true" {
		r.Static("/static", "./dist")
	}

	r.GET("/", index)
	r.GET("/albums", albumsHandler)
	r.GET("/albums/:album/:song", songsHandler)
	r.GET("/love-it", loveIt)
	r.GET("/preorder", preorderIndex)
	r.POST("/preorder", postCreateOrder)
	r.POST("/preorder/bestellen", postGetOrderInfo)
	r.POST("/preorder/webhook", postMollieWebhook)
	r.GET("/preorder/bedankt", getPreOrderThankYou)
	r.POST("/preorder/retry/:orderid", postOrderRetry)
	r.GET("/preorder/pending/:orderid", getPendingOrder)
	r.NoRoute(handle404)

	sockPath := path.Join(ROOT_DIR, "run/flip.sock")
	sock := createSocket(sockPath)

	server := http.Server{
		Handler: r,
	}

	fmt.Println("Running...")
	go func() {
		log.Fatal(server.Serve(sock))
	}()

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)

	// Block until a signal is received.
	<-c
	fmt.Println("Stopping...")
	os.Remove(path.Join(ROOT_DIR, "run/flip.sock"))
}
