package universities

import (
	"context"
	"fmt"
	"slices"
	"strings"
	"sync"

	"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 RestaurantHandler struct {
	QueryClient           interfaces.QueryClient[stwbremen.RestaurantList]
	QueryClientRestaurant interfaces.QueryClient[stwbremen.Restaurant]
}

func (h *RestaurantHandler) Get(ctx context.Context, name string, params params.Container) (*interfaces.ResourceList, error) {
	var list []string = []string{name}

	// If no name provided, get list of all available restaurants
	if name == "" {
		fetchedNames, err := getRestaurantList(ctx, h.QueryClient)
		if err != nil {
			return nil, err
		}

		list = fetchedNames
	}

	// Get restaurant information in parallel
	var wg sync.WaitGroup
	var mutex sync.Mutex
	restaurants := make([]resources.Restaurant, 0, len(list))
	for _, name := range list {
		wg.Add(1)
		go func() {
			defer wg.Done()

			restaurant, err := h.getRestaurantInfo(ctx, name)

			// Append restaurant in critical section
			if err == nil {
				mutex.Lock()
				restaurants = append(restaurants, *restaurant)
				mutex.Unlock()
			}
		}()
	}
	wg.Wait()

	// Sort by name
	slices.SortFunc(restaurants, func(a, b resources.Restaurant) int { return strings.Compare(a.Name, b.Name) })

	// Return
	return &interfaces.ResourceList{
		ItemKind: resources.ResourceRestaurant,
		Items:    util.Transform(restaurants, func(i *resources.Restaurant) interfaces.Resource { return i }),
	}, nil

}

func (h *RestaurantHandler) getRestaurantInfo(ctx context.Context, name string) (*resources.Restaurant, error) {
	r, err := h.QueryClientRestaurant.Get(ctx,
		fmt.Sprintf("page('essen-und-trinken/%s')", name),
		`{"title": "page.title","id": "page.content.mensalocationid","image": "page.files.first().url","address": "page.content.address","openingTimes": {"query": "page.openingTimes.toStructure()","select": {"weekday": "structureItem.day","openingTime": "structureItem.open","closingTime": "structureItem.close"}},"offseasonOpeningTimes": {"query": "page.offseasonOpeningTimes.toStructure()","select": {"weekday": "structureItem.day","openingTime": "structureItem.open","closingTime": "structureItem.close"}},"offseasonStart": true,"offseasonEnd": true,"changedTimes": {"query": "page.changedTimes.toStructure()","select": {"startDate": "structureItem.startChangedDate","endDate": "structureItem.endChangedDate","openingTime": "structureItem.openChangedTime","closingTime": "structureItem.closedChangedTime"}}}`,
		false,
	)
	if err != nil {
		return nil, fmt.Errorf("failed to load restaurant %s: %w", name, err)
	}

	if r.Title == "" {
		return nil, fmt.Errorf("restaurant not found: %s", name)
	}

	return &resources.Restaurant{
		Name:         name,
		Title:        r.Title,
		ID:           r.ID,
		Address:      r.Address,
		Image:        r.Image,
		OpeningHours: r.OpeningHours,
	}, nil
}

func (h *RestaurantHandler) GetParametersForVerb(verb interfaces.Verb) []params.Registration {
	return []params.Registration{}
}
