package booking

import (
	"context"
	"fmt"
	"net/http"
	"strconv"
	"strings"

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

type filterForm struct {
	company       *auth.Company
	HolderName    *form.Input
	BookingStatus *form.Select
	FromDate      *form.Input
	ToDate        *form.Input
	Cursor        *form.Cursor
}

func newFilterForm(ctx context.Context, conn *database.Conn, company *auth.Company, locale *locale.Locale) *filterForm {
	return &filterForm{
		company: company,
		HolderName: &form.Input{
			Name: "holder_name",
		},
		BookingStatus: &form.Select{
			Name:    "booking_status",
			Options: mustGetBookingStatusOptions(ctx, conn, locale),
		},
		FromDate: &form.Input{
			Name: "from_date",
		},
		ToDate: &form.Input{
			Name: "to_date",
		},
		Cursor: &form.Cursor{
			Name:    "cursor",
			PerPage: 25,
		},
	}
}

func mustGetBookingStatusOptions(ctx context.Context, conn *database.Conn, locale *locale.Locale) []*form.Option {
	return form.MustGetOptions(ctx, conn, `
		select booking_status.booking_status
		     , isi18n.name 
		from booking_status
		    join booking_status_i18n isi18n using(booking_status)
		where isi18n.lang_tag = $1
		  and booking_status <> 'created'
		order by booking_status`, locale.Language)
}

func (f *filterForm) Parse(r *http.Request) error {
	if err := r.ParseForm(); err != nil {
		return err
	}
	f.HolderName.FillValue(r)
	f.BookingStatus.FillValue(r)
	f.FromDate.FillValue(r)
	f.ToDate.FillValue(r)
	f.Cursor.FillValue(r)
	return nil
}

func (f *filterForm) BuildQuery(args []interface{}) (string, []interface{}) {
	var where []string
	appendWhere := func(expression string, value interface{}) {
		args = append(args, value)
		where = append(where, fmt.Sprintf(expression, len(args)))
	}
	maybeAppendWhere := func(expression string, value string, conv func(string) interface{}) {
		if value != "" {
			if conv == nil {
				appendWhere(expression, value)
			} else {
				appendWhere(expression, conv(value))
			}
		}
	}

	appendWhere("booking.company_id = $%d", f.company.ID)
	maybeAppendWhere("booking.holder_name ILIKE $%d", f.HolderName.Val, func(v string) interface{} {
		return "%" + v + "%"
	})
	if len(f.BookingStatus.Selected) == 0 {
		where = append(where, "booking.booking_status <> 'created'")
	} else {
		maybeAppendWhere("booking.booking_status = $%d", f.BookingStatus.String(), nil)
	}
	maybeAppendWhere("lower(stay) >= $%d", f.FromDate.Val, nil)
	maybeAppendWhere("lower(stay) <= $%d", f.ToDate.Val, nil)

	if f.Paginated() {
		params := f.Cursor.Params()
		if len(params) == 2 {
			where = append(where, fmt.Sprintf("(lower(stay), booking_id) < ($%d, $%d)", len(args)+1, len(args)+2))
			args = append(args, params[0])
			args = append(args, params[1])
		}
	}

	return strings.Join(where, ") AND ("), args
}

func (f *filterForm) buildCursor(bookings []*bookingEntry) []*bookingEntry {
	return form.BuildCursor(f.Cursor, bookings, func(entry *bookingEntry) []string {
		return []string{entry.ArrivalDate.Format(database.ISODateFormat), strconv.Itoa(entry.ID)}
	})
}

func (f *filterForm) HasValue() bool {
	return f.HolderName.Val != "" ||
		(len(f.BookingStatus.Selected) > 0 && f.BookingStatus.Selected[0] != "") ||
		f.FromDate.Val != "" ||
		f.ToDate.Val != ""
}

func (f *filterForm) PerPage() int {
	return f.Cursor.PerPage
}

func (f *filterForm) Paginated() bool {
	return f.Cursor.Pagination
}
