package main

import (
	"bytes"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"path"
	"time"

	"github.com/gin-gonic/gin"
	"gopkg.in/gomail.v2"
)

const SHIPPING = 250

func sendMail(preOrder PreOrder, config Config) {
	tpl, err := template.ParseFiles(path.Join(ROOT_DIR, "templates/preorder.email"))

	if err != nil {
		panic(err)
	}
	m := gomail.NewMessage()

	var w bytes.Buffer
	data := map[string]interface{}{
		"preOrder": preOrder,
	}

	tpl.Execute(&w, data)

	m.SetHeader("From", config.FromAddr)
	m.SetHeader("To", preOrder.Email)
	m.SetAddressHeader("Bcc", config.BCC, "admin")
	m.SetHeader("Subject", "Flip Noorman bestelling | Love it!")
	m.SetBody("text/html", w.String())

	d := gomail.NewDialer(config.MailServer, config.MailPort, config.MailUser, config.MailPass)

	// Send the email to Bob, Cora and Dan.
	if err := d.DialAndSend(m); err != nil {
		panic(err)
	}
}

func preorderIndex(c *gin.Context) {
	db := getDB(c)
	products := []Product{}
	db.Where("id = 2").Find(&products)

	c.HTML(http.StatusOK, "preorder_index.html", map[string]interface{}{
		"products": products,
		"nowYear":  time.Now().Format("2006"),
	})
}
func postGetOrderInfo(c *gin.Context) {
	db := getDB(c)

	var order struct {
		CD int `form:"product[2]"`
	}

	c.Bind(&order)

	product := Product{}
	db.Where("id = 2").Find(&product)

	subtotal := product.Price * order.CD
	shipping := SHIPPING
	total := subtotal + shipping

	c.HTML(http.StatusOK, "preorder_info.html", map[string]interface{}{
		"orderCount": order.CD,
		"product":    product,
		"subtotal":   subtotal,
		"shipping":   shipping,
		"total":      total,
		"error_":     "",
	})
}

func renderGetOrderInfoError(error_ string, count int, c *gin.Context) {
	db := getDB(c)
	product := Product{}
	db.Where("id = 2").Find(&product)

	subtotal := product.Price * count
	shipping := SHIPPING
	total := subtotal + shipping

	c.HTML(http.StatusOK, "preorder_info.html", map[string]interface{}{
		"orderCount": count,
		"product":    product,
		"subtotal":   subtotal,
		"shipping":   shipping,
		"total":      total,
		"error_":     error_,
	})
}

func postCreateOrder(c *gin.Context) {
	db := getDB(c)
	products := []Product{}
	db.Find(&products)

	var order struct {
		DVD      int    `form:"product_count"`
		Name     string `form:"name"`
		Email    string `form:"email"`
		Address  string `form:"address"`
		Postcode string `form:"postcode"`
		City     string `form:"city"`
	}

	c.Bind(&order)

	if order.Name == "" || order.Email == "" || order.Address == "" || order.Postcode == "" || order.City == "" || order.DVD < 0 {
		renderGetOrderInfoError("Alle velden zijn verplicht", order.DVD, c)
		return
	}

	preOrder := PreOrder{
		Name:     order.Name,
		Email:    order.Email,
		Address:  order.Address,
		Postcode: order.Postcode,
		City:     order.City,
		Status:   PRE_ORDER_STATE_CREATED,
	}

	db.Create(&preOrder)

	for i := 0; i < order.DVD; i++ {
		db.Create(&Item{
			ProductID:  2,
			PreOrderID: preOrder.ID,
		})
	}

	db.Preload("Items").Preload("Items.Product").Find(&preOrder)

	payment, err := createMolliePayment(preOrder)

	if err != nil {
		log.Printf("[err] createMolliePayment: %v", err)
		c.AbortWithError(500, fmt.Errorf("Er is iets fout gegaan..."))
		return
	}

	molliePayment := MolliePayment{
		MolliePaymentID: payment.ID,
		Amount:          preOrder.CalcTotal() + SHIPPING,
		Status:          payment.Status,
		PreOrderID:      preOrder.ID,
	}

	db.Create(&molliePayment)

	c.Redirect(http.StatusFound, payment.Links.Checkout.Href)
}

func postMollieWebhook(c *gin.Context) {
	db := getDB(c)

	paymentID := c.Request.FormValue("id")

	payment, err := mollieClient.Payments.Get(paymentID, nil)

	if err != nil {
		log.Printf("Error get payment from mollie; %v; %s", err, paymentID)
		c.AbortWithError(500, fmt.Errorf("Deze bestelling is onbekend"))
		return
	}

	molliePayment := &MolliePayment{}

	if err := db.Preload("PreOrder").Where("mollie_payment_id = ?", paymentID).Find(&molliePayment).Error; err != nil {
		log.Printf("Error get payment from db; %v; %s", err, paymentID)
		c.AbortWithError(500, fmt.Errorf("Deze bestelling is onbekend"))
		return
	}

	if payment.Status != molliePayment.Status {
		molliePayment.Status = payment.Status
		db.Save(&molliePayment)

		if molliePayment.Status == "paid" {
			sendMail(molliePayment.PreOrder, config)
		}
	}

	c.Status(200)
}

func postOrderRetry(c *gin.Context) {
	orderID := c.Param("orderid")

	db := getDB(c)

	preOrder := &PreOrder{}

	if err := db.Preload("Items").Preload("Items.Product").Where("id = ?", orderID).Find(&preOrder).Error; err != nil {
		c.AbortWithError(500, fmt.Errorf("Deze bestelling is onbekend"))
		return
	}

	db.Find(&preOrder)

	payment, err := createMolliePayment(*preOrder)

	if err != nil {
		c.AbortWithError(500, fmt.Errorf("Er is iets fout gegaan..."))
		return
	}

	molliePayment := MolliePayment{
		MolliePaymentID: payment.ID,
		Amount:          preOrder.CalcTotal() + SHIPPING,
		Status:          payment.Status,
		PreOrderID:      preOrder.ID,
	}

	db.Create(&molliePayment)

	c.Redirect(http.StatusFound, payment.Links.Checkout.Href)
}

func getPendingOrder(c *gin.Context) {
	orderID := c.Param("orderid")

	db := getDB(c)

	payments := []*MolliePayment{}

	if err := db.Where("pre_order_id = ?", orderID).Order("created_at desc").Find(&payments).Error; err != nil {
		c.AbortWithError(500, fmt.Errorf("Deze bestelling is onbekend"))
		return
	}

	payment := payments[0]

	if payment.Status == "paid" {
		c.Redirect(http.StatusFound, "/preorder/bedankt")
		return
	}

	c.HTML(http.StatusOK, "preorder_pending.html", map[string]interface{}{"payment": payment, "orderID": orderID})
}

func getPreOrderThankYou(c *gin.Context) {
	c.HTML(http.StatusOK, "preorder_thankyou.html", nil)
}
