package meals

import (
	"context"
	"fmt"
	"slices"
	"strings"
	"time"

	"git.bissendorf.co/bissendorf/unifood/m/v2/core/handler"
	"git.bissendorf.co/bissendorf/unifood/m/v2/core/interfaces"
	"git.bissendorf.co/bissendorf/unifood/m/v2/core/interfaces/params"
	"git.bissendorf.co/bissendorf/unifood/m/v2/model/external/stwbremen"
	"git.bissendorf.co/bissendorf/unifood/m/v2/model/resources"
	"git.bissendorf.co/bissendorf/unifood/m/v2/util"
)

type MealsHandler struct {
	interfaces.ResourceHandler
	interfaces.GetHandler

	QueryClient interfaces.QueryClient[[]stwbremen.Meal]
}

func (h *MealsHandler) Get(ctx context.Context, name string, params params.Container) (*interfaces.ResourceList, error) {
	// Read parameters
	date, err := params.GetValue(handler.ParamDate)
	if err != nil {
		return nil, fmt.Errorf("unable to parse date parameter: %w", err)
	}

	restaurant, err := params.GetValue(handler.ParamRestaurant)
	if err != nil {
		return nil, fmt.Errorf("unable to parse restaurant parameter: %w", err)
	}

	// Build query
	query := "page('meals').children.filterBy('printonly', 0)"
	if date != "" {
		parsed, err := time.Parse(time.DateOnly, date.(string))
		if err != nil {
			return nil, fmt.Errorf("unable to parse date parameter: %w", err)
		}
		query += fmt.Sprintf(".filterBy('date', '%s')", parsed.Format(time.DateOnly))
	}
	if restaurant != "" {
		query += fmt.Sprintf(".filterBy('location', '%s')", restaurant.(string))
	}
	if name != "" {
		query += fmt.Sprintf(".filterBy('id', 'meals/%s')", name)
	}

	// Run query
	meals, err := h.QueryClient.Get(ctx,
		query,
		`{"title":true,"id":"page.id","ingredients":"page.ingredients.toObject","prices":"page.prices.toObject","location":true,"counter":true,"date":true,"mealadds":true,"mark":true,"frei3":true,"printonly":true,"kombicategory":true,"categories":"page.categories.split"}`,
		false,
	)
	if err != nil {
		return nil, fmt.Errorf("querying menu failed: %w", err)
	}

	// Transform to meal resource
	items := util.Transform(*meals, func(i *stwbremen.Meal) *resources.Meal {
		d, err := resources.MealFromDTO(*i)
		if err != nil {
			return &resources.Meal{}
		}

		return d
	})

	// Sort by date, restaurant and title - need to apply stable sorts in reverse order to achieve this
	slices.SortFunc(items, func(a, b *resources.Meal) int {
		return strings.Compare(a.Title, b.Title)
	})
	slices.SortStableFunc(items, func(a, b *resources.Meal) int {
		return strings.Compare(a.RestaurantID, b.RestaurantID)
	})
	slices.SortStableFunc(items, func(a, b *resources.Meal) int {
		return a.Date.Compare(b.Date)
	})

	// Return
	return &interfaces.ResourceList{
		ItemKind: resources.ResourceMeal,
		Items:    util.Transform(items, func(i **resources.Meal) interfaces.Resource { return *i }),
	}, nil
}

func (h *MealsHandler) GetParametersForVerb(verb interfaces.Verb) []params.Registration {
	return []params.Registration{
		{
			Name:        handler.ParamDate,
			ShortHand:   "d",
			Description: "Meal date",
			DefaultFunc: func() string { return "" },
			ParseFunc:   params.ParseString,
		},
		{
			Name:        handler.ParamRestaurant,
			ShortHand:   "r",
			Description: "Select a restaurant",
			DefaultFunc: func() string { return "" },
			ParseFunc:   params.ParseString,
		},
	}
}
