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       config.Platform
	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:       config.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) ToInvoice(hc *config.Host) Invoice {
	total := u.Reduce(b.Items, func(i Item, sum float64) float64 {
		return sum + i.Price*float64(i.Quantity)
	}, 0.0)
	amountPaid := u.Reduce(b.Payments, func(i Payment, sum float64) float64 {
		return sum + i.Amount
	}, 0.0)

	return Invoice{
		Host:            *hc,
		Name:            b.Name,
		PhoneNumber:     b.PhoneNumber,
		CustomersNumber: b.CustomerNumber,
		Platform:        b.Platform.ToFrench(),
		ID:              b.InvoiceNumber(hc),
		From:            b.From.Format("02/01/2006"),
		To:              b.To.Format("02/01/2006"),
		Total:           strconv.FormatFloat(total, 'f', 2, 64),
		AmountPaid:      strconv.FormatFloat(amountPaid, 'f', 2, 64),
		BalanceDue:      strconv.FormatFloat(total-amountPaid, 'f', 2, 64),
		Lines: u.Map(b.Items, func(i Item) InvoiceLine {
			return InvoiceLine{
				Name:     i.ToFrench(),
				Quantity: i.Quantity,
				Price:    strconv.FormatFloat(i.Price, 'f', 2, 64),
				Total:    strconv.FormatFloat(i.Price*float64(i.Quantity), 'f', 2, 64),
			}
		}),
		Payments: u.Map(b.Payments, func(p Payment) PaymentLine {
			return PaymentLine{
				Date:   p.CreatedAt.Format("02/01/2006"),
				Method: p.PaymentMethod.ToFrench(),
				Amount: strconv.FormatFloat(p.Amount, '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 config.PaymentMethod
}
