package booking

import (
	"fmt"
	"strconv"
	"time"

	u "github.com/rjNemo/underscore"
	"gorm.io/gorm"

	"github.com/rjNemo/rentease/internal/config"
)

type Booking struct {
	From time.Time
	To   time.Time
	gorm.Model
	Name           string `gorm:"column:customer_name"`
	PhoneNumber    string
	Email          string
	Platform       string
	ExternalId     *string `gorm:"uniqueIndex:booking_external_id"`
	Items          []Item
	Payments       []Payment `gorm:"foreignKey:BookingID"`
	Id             int
	CustomerNumber int     `gorm:"column:customers"`
	PlatformFees   float64 `gorm:"type:decimal(10,2)"`
	Canceled       bool    `gorm:"default:false"`
}

// NewBooking creates a new booking with the given parameters
func NewBooking(from, to time.Time, name, phoneNumber, email, platform string,
	customerNumber int, platformFees float64, externalId *string,
) *Booking {
	return &Booking{
		From:           from,
		To:             to,
		Name:           name,
		PhoneNumber:    phoneNumber,
		CustomerNumber: customerNumber,
		Email:          email,
		Platform:       platform,
		PlatformFees:   platformFees,
		ExternalId:     externalId,
	}
}

func (b Booking) InvoiceNumber(hc *config.Host) string {
	return fmt.Sprintf("%s%04s", hc.InvoicePrefix, strconv.Itoa(b.Id+hc.CustomerSeed))
}

func (b Booking) Serialize(hc *config.Host) map[string]any {
	return map[string]any{
		"host": map[string]any{
			"name":    hc.Name,
			"address": hc.Address,
			"zip":     hc.ZipCode,
			"city":    hc.City,
			"phone":   hc.PhoneNumber,
			"email":   hc.Email,
		},
		"id":               b.InvoiceNumber(hc),
		"name":             b.Name,
		"phone_number":     b.PhoneNumber,
		"customers_number": b.CustomerNumber,
		"platform":         b.Platform,
		"from":             b.From.Format("02/01/2006"),
		"to":               b.To.Format("02/01/2006"),
		"lines": u.Map(b.Items, func(i Item) map[string]any {
			return map[string]any{
				"name":     i.ToFrench(),
				"quantity": i.Quantity,
				"price":    i.Price,
				"total":    i.Price * float64(i.Quantity),
			}
		}),
		"total": strconv.FormatFloat(u.Reduce(b.Items, func(i Item, sum float64) float64 {
			return sum + i.Price*float64(i.Quantity)
		}, 0.0), 'f', 2, 64),
	}
}

// WithId returns a copy of the booking with the given ID
func (b *Booking) WithId(id int) *Booking {
	b.Id = id
	return b
}

type Item struct {
	gorm.Model
	Item          string
	PaymentMethod string
	PaymentStatus string `gorm:"default:Pending"`
	Id            int
	BookingId     int
	Quantity      int
	Price         float64 `gorm:"type:decimal(10,2)"`
}

// ToFrench translates the item name to french
func (i Item) ToFrench() string {
	switch i.Item {
	case "T2":
		return "T2"
	case "T3":
		return "T3"
	case "Airport":
		return "Transport Aéroport"
	case "Port":
		return "Transport Gare Maritime"
	case "Transport":
		return "Transport"
	case "Taxes":
		return "Taxes de sejour"
	default:
		return i.Item
	}
}

type Payment struct {
	gorm.Model
	BookingID     uint    `gorm:"not null;index"`
	Booking       Booking `gorm:"foreignKey:BookingID;constraint:OnDelete:CASCADE"`
	Amount        float64
	PaymentMethod string
}
