package server

import (
	"fmt"
	"net/http"

	"github.com/bouk/httprouter"
	"github.com/influxdata/influxdb/v2/chronograf"
)

type link struct {
	Href string `json:"href"`
	Rel  string `json:"rel"`
}

type layoutResponse struct {
	chronograf.Layout
	Link link `json:"link"`
}

func newLayoutResponse(layout chronograf.Layout) layoutResponse {
	httpAPILayouts := "/chronograf/v1/layouts"
	href := fmt.Sprintf("%s/%s", httpAPILayouts, layout.ID)
	rel := "self"

	for idx, cell := range layout.Cells {
		axes := []string{"x", "y", "y2"}

		if cell.Axes == nil {
			layout.Cells[idx].Axes = make(map[string]chronograf.Axis, len(axes))
		}

		if cell.CellColors == nil {
			layout.Cells[idx].CellColors = []chronograf.CellColor{}
		}

		for _, axis := range axes {
			if _, found := cell.Axes[axis]; !found {
				layout.Cells[idx].Axes[axis] = chronograf.Axis{
					Bounds: []string{},
				}
			}
		}
	}

	return layoutResponse{
		Layout: layout,
		Link: link{
			Href: href,
			Rel:  rel,
		},
	}
}

type getLayoutsResponse struct {
	Layouts []layoutResponse `json:"layouts"`
}

// Layouts retrieves all layouts from store
func (s *Service) Layouts(w http.ResponseWriter, r *http.Request) {
	// Construct a filter sieve for both applications and measurements
	filtered := map[string]bool{}
	for _, a := range r.URL.Query()["app"] {
		filtered[a] = true
	}

	for _, m := range r.URL.Query()["measurement"] {
		filtered[m] = true
	}

	ctx := r.Context()
	layouts, err := s.Store.Layouts(ctx).All(ctx)
	if err != nil {
		Error(w, http.StatusInternalServerError, "Error loading layouts", s.Logger)
		return
	}

	filter := func(layout *chronograf.Layout) bool {
		// If the length of the filter is zero then all values are acceptable.
		if len(filtered) == 0 {
			return true
		}

		// If filter contains either measurement or application
		return filtered[layout.Measurement] || filtered[layout.Application]
	}

	res := getLayoutsResponse{
		Layouts: []layoutResponse{},
	}

	seen := make(map[string]bool)
	for _, layout := range layouts {
		// remove duplicates
		if seen[layout.Measurement+layout.ID] {
			continue
		}
		// filter for data that belongs to provided application or measurement
		if filter(&layout) {
			res.Layouts = append(res.Layouts, newLayoutResponse(layout))
		}
	}
	encodeJSON(w, http.StatusOK, res, s.Logger)
}

// LayoutsID retrieves layout with ID from store
func (s *Service) LayoutsID(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	id := httprouter.GetParamFromContext(ctx, "id")

	layout, err := s.Store.Layouts(ctx).Get(ctx, id)
	if err != nil {
		Error(w, http.StatusNotFound, fmt.Sprintf("ID %s not found", id), s.Logger)
		return
	}

	res := newLayoutResponse(layout)
	encodeJSON(w, http.StatusOK, res, s.Logger)
}
