package booking

import (
	"context"
	"strconv"
	"time"

	"dev.tandem.ws/tandem/camper/pkg/database"
	"dev.tandem.ws/tandem/camper/pkg/locale"
)

type bookingCart struct {
	Lines   []*cartLine
	Total   string
	Enabled bool
}

type cartLine struct {
	Concept  string
	Units    int
	Subtotal string
}

func newBookingCart(ctx context.Context, conn *database.Conn, f *bookingForm, campsiteType string) (*bookingCart, error) {
	cart := &bookingCart{
		Total: "0.0",
	}
	if f.Dates == nil {
		return cart, nil
	}
	arrivalDate, err := time.Parse(database.ISODateFormat, f.Dates.ArrivalDate.Val)
	if err != nil {
		return cart, nil
	}
	departureDate, err := time.Parse(database.ISODateFormat, f.Dates.DepartureDate.Val)
	if err != nil {
		return cart, nil
	}

	if f.Guests == nil {
		return cart, nil
	}
	numAdults, err := strconv.Atoi(f.Guests.NumberAdults.Val)
	if err != nil {
		return cart, nil
	}
	numTeenagers, err := strconv.Atoi(f.Guests.NumberTeenagers.Val)
	if err != nil {
		return cart, nil
	}
	numChildren, err := strconv.Atoi(f.Guests.NumberChildren.Val)
	if err != nil {
		return cart, nil
	}
	numDogs := 0
	if f.Guests.NumberDogs != nil {
		numDogs, err = strconv.Atoi(f.Guests.NumberDogs.Val)
		if err != nil {
			return cart, nil
		}
	}
	zonePreferences := ""
	if f.Options != nil && f.Options.ZonePreferences != nil {
		zonePreferences = f.Options.ZonePreferences.Val
	}

	optionMap := make(map[int]*campsiteTypeOption)
	var typeOptions []*campsiteTypeOption
	if f.Options != nil {
		typeOptions = f.Options.Options
	}
	optionUnits := make([]*database.OptionUnits, 0, len(typeOptions))
	for _, option := range typeOptions {
		units, _ := strconv.Atoi(option.Input.Val)
		if units < 1 {
			continue
		}
		optionMap[option.ID] = option
		optionUnits = append(optionUnits, &database.OptionUnits{
			OptionID: option.ID,
			Units:    units,
		})
	}

	paymentSlug, err := conn.DraftPayment(
		ctx,
		f.PaymentSlug.Val,
		arrivalDate,
		departureDate,
		campsiteType,
		numAdults,
		numTeenagers,
		numChildren,
		numDogs,
		zonePreferences,
		optionUnits,
	)
	if err != nil {
		return nil, err
	}
	f.PaymentSlug.Val = paymentSlug

	row := conn.QueryRow(ctx, `
		select payment_id
		     , departure_date - arrival_date
		     , to_price(subtotal_nights, decimal_digits)
		     , to_price(subtotal_adults, decimal_digits)
		     , to_price(subtotal_teenagers, decimal_digits)
		     , to_price(subtotal_children, decimal_digits)
		     , to_price(subtotal_dogs, decimal_digits)
		     , to_price(subtotal_tourist_tax, decimal_digits)
		     , to_price(total, decimal_digits)
		from payment
			join company using (company_id)
			join currency using (currency_code)
		where payment.slug = $1
`, paymentSlug)

	var paymentID int
	var numNights int
	var nights string
	var adults string
	var teenagers string
	var children string
	var dogs string
	var touristTax string
	var total string
	if err = row.Scan(&paymentID, &numNights, &nights, &adults, &teenagers, &children, &dogs, &touristTax, &total); err != nil {
		if database.ErrorIsNotFound(err) {
			return cart, nil
		}
		return nil, err
	}

	maybeAddLine := func(units int, subtotal string, concept string) {
		if units > 0 && subtotal != "" {
			cart.Lines = append(cart.Lines, &cartLine{
				Concept:  concept,
				Units:    units,
				Subtotal: subtotal,
			})
		}
	}
	maybeAddLine(numNights, nights, locale.PgettextNoop("Night", "cart"))
	maybeAddLine(numAdults, adults, locale.PgettextNoop("Adult", "cart"))
	maybeAddLine(numTeenagers, teenagers, locale.PgettextNoop("Teenager", "cart"))
	maybeAddLine(numChildren, children, locale.PgettextNoop("Child", "cart"))
	maybeAddLine(numDogs, dogs, locale.PgettextNoop("Dog", "cart"))

	rows, err := conn.Query(ctx, `
		select campsite_type_option_id
		     , units
		     , to_price(subtotal, decimal_digits)
		from payment_option
		   join payment using (payment_id)
		   join company using (company_id)
		   join currency using (currency_code)
		where payment_id = $1
`, paymentID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	for rows.Next() {
		var optionID int
		var units int
		var subtotal string
		err = rows.Scan(&optionID, &units, &subtotal)
		if err != nil {
			return nil, err
		}
		option := optionMap[optionID]
		if option == nil {
			continue
		}
		maybeAddLine(units, subtotal, option.Label)
	}
	if rows.Err() != nil {
		return nil, rows.Err()
	}

	maybeAddLine(numAdults, touristTax, locale.PgettextNoop("Tourist tax", "cart"))

	if total != "0.0" {
		cart.Total = total
		cart.Enabled = f.Guests.Error == nil
	}

	return cart, nil
}
