package models

import (
	"fmt"
	"gorm.io/gorm"
)

type OrderStatus string

const (
	AwaitingConfirmation OrderStatus = "AwaitingConfirmation"
	Received             OrderStatus = "Received"
	AwaitingPayment      OrderStatus = "AwaitingPayment"
	Payed                OrderStatus = "Payed"
	ReadyForPickup       OrderStatus = "ReadyForPickup"
	Shipped              OrderStatus = "Shipped"
	Cancelled            OrderStatus = "Cancelled"
)

type AddressInfo struct {
	FirstName  string `json:"firstname"`
	LastName   string `json:"lastname"`
	Address    string `json:"address"`
	PostalCode string `json:"postalcode"`
	City       string `json:"city"`
	Country    string `json:"country"`
}

type Shipping struct {
	Id    string  `json:"id"`
	Name  string  `json:"name"`
	Price float64 `json:"price"`
}

func GetShippingMethods() []Shipping {
	return []Shipping{
		{Id: "germany", Name: "Germany (DHL)", Price: 3.99},
		{Id: "international", Name: "International (DHL)", Price: 5.99},
		{Id: "pickup", Name: "Pickup", Price: 0.00},
	}
}

func GetShippingMethod(id string) (Shipping, error) {
	var shipping Shipping
	found := false
	for _, shippingMethod := range GetShippingMethods() {
		if shippingMethod.Id == id {
			shipping = shippingMethod
			found = true
		}
	}

	if !found {
		return Shipping{}, fmt.Errorf("Shipping method does not exist.")
	}

	return shipping, nil
}

type Order struct {
	gorm.Model
	SessionId  string      `json:"sessionid" binding:"required" gorm:"not null"`
	Status     OrderStatus `json:"status"`
	Token      string      `json:"token" binding:"required" gorm:"not null"`
	Email      string      `json:"email"`
	Comment    string      `json:"comment"`
	FirstName  string      `json:"firstname"`
	LastName   string      `json:"lastname"`
	Address    string      `json:"address"`
	PostalCode string      `json:"postalcode"`
	City       string      `json:"city"`
	Country    string      `json:"country"`
	Shipping   string      `json:"shipping"`
	CartItems  []CartItem  `json:"cartitems" gorm:"foreignKey:OrderID"`
}

func (o *Order) Validate() error {
	//TODO: validate sessionId
	if o.SessionId == "" {
		return fmt.Errorf("Invalid SessionId")
	}

	//TODO: validate token
	if o.Token == "" {
		return fmt.Errorf("Invalid Token")
	}

	if o.Status == AwaitingConfirmation {
		return fmt.Errorf("Order still awaiting confirmation.")
	}

	if len(o.CartItems) == 0 {
		return fmt.Errorf("Order is empty.")
	}

	shipping, err := GetShippingMethod(o.Shipping)

	if err != nil {
		return err
	}

	//for pickup no address validation is necessary
	if shipping.Id == "pickup" {
		return nil
	}

	return o.ValidateAddress()
}

func (o *Order) ValidateAddress() error {
	if o.FirstName == "" {
		return fmt.Errorf("Firstname missing")
	}
	if o.LastName == "" {
		return fmt.Errorf("Lastname missing")
	}

	if o.Address == "" {
		return fmt.Errorf("Address missing")
	}

	if o.PostalCode == "" {
		return fmt.Errorf("Postalcode missing")
	}

	if o.City == "" {
		return fmt.Errorf("City missing")
	}

	if o.Country == "" {
		return fmt.Errorf("Country missing")
	}
	return nil
}

func (o *Order) CalculatePrices() (float64, float64, error) {
	shipping, err := GetShippingMethod(o.Shipping)
	if err != nil {
		return 0, 0, err
	}

	priceProducts := 0.0
	for _, cartItem := range o.CartItems {
		priceProducts += (float64(cartItem.Quantity) * cartItem.ItemVariant.Price)
	}

	priceTotal := priceProducts + shipping.Price

	return priceProducts, priceTotal, nil
}

type CartItem struct {
	gorm.Model
	SessionId     string `json:"sessionid" binding:"required" gorm:"not null"`
	ShopItemId    uint
	ShopItem      ShopItem `json:"shopitem" gorm:"foreignKey:ShopItemId"` //gorm one2one
	ItemVariantId uint
	ItemVariant   ItemVariant `json:"itemvariant" gorm:"foreignKey:ItemVariantId"` //gorm one2one
	Quantity      int         `json:"quantity" binding:"required"`
	OrderID       uint
}
