package homepageapi

import (
	"fmt"
	"net/http"
	"net/url"
	"slices"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/lithammer/fuzzysearch/fuzzy"
	entrypoint "github.com/yusing/godoxy/internal/entrypoint/types"
	"github.com/yusing/godoxy/internal/homepage"
	apitypes "github.com/yusing/goutils/apitypes"
	"github.com/yusing/goutils/http/httpheaders"
	"github.com/yusing/goutils/http/websocket"
)

type HomepageItemsRequest struct {
	SearchQuery string `form:"search"`   // Search query
	Category    string `form:"category"` // Category filter
	Provider    string `form:"provider"` // Provider filter
	// Sort method
	SortMethod homepage.SortMethod `form:"sort_method" default:"alphabetical" binding:"omitempty,oneof=clicks alphabetical custom"`
} //	@name	HomepageItemsRequest

// @x-id				"items"
// @BasePath		/api/v1
// @Summary		Homepage items
// @Description	Homepage items
// @Tags			homepage,websocket
// @Accept			json
// @Produce		json
// @Param			query		query		HomepageItemsRequest	false	"Query parameters"
// @Success		200			{object}	homepage.Homepage
// @Failure		400			{object}	apitypes.ErrorResponse
// @Failure		403			{object}	apitypes.ErrorResponse
// @Router			/homepage/items [get]
func Items(c *gin.Context) {
	var request HomepageItemsRequest
	if err := c.ShouldBindQuery(&request); err != nil {
		c.JSON(http.StatusBadRequest, apitypes.Error("invalid request", err))
		return
	}

	proto := "http"
	if c.Request.TLS != nil || c.GetHeader("X-Forwarded-Proto") == "https" {
		proto = "https"
	}
	hostname := c.Request.Host
	if host := c.GetHeader("X-Forwarded-Host"); host != "" {
		hostname = host
	}

	ep := entrypoint.FromCtx(c.Request.Context())
	if httpheaders.IsWebsocket(c.Request.Header) {
		websocket.PeriodicWrite(c, 2*time.Second, func() (any, error) {
			return HomepageItems(ep, proto, hostname, &request), nil
		})
	} else {
		c.JSON(http.StatusOK, HomepageItems(ep, proto, hostname, &request))
	}
}

func HomepageItems(ep entrypoint.Entrypoint, proto, hostname string, request *HomepageItemsRequest) homepage.Homepage {
	switch proto {
	case "http", "https":
	default:
		proto = "http"
	}

	hp := homepage.NewHomepageMap(ep.HTTPRoutes().Size())

	if strings.Count(hostname, ".") > 1 {
		_, hostname, _ = strings.Cut(hostname, ".") // remove the subdomain
	}

	for _, r := range ep.HTTPRoutes().Iter {
		if request.Provider != "" && r.ProviderName() != request.Provider {
			continue
		}
		item := r.HomepageItem()
		if request.Category != "" && item.Category != request.Category {
			continue
		}
		if request.SearchQuery != "" && !fuzzy.MatchFold(request.SearchQuery, item.Name) {
			continue
		}

		// clear url if invalid
		_, err := url.Parse(item.URL)
		if err != nil {
			item.URL = ""
		}

		// append hostname if provided and only if alias is not FQDN
		if hostname != "" && item.URL == "" {
			isFQDNAlias := strings.Contains(item.Alias, ".")
			if !isFQDNAlias {
				item.URL = fmt.Sprintf("%s://%s.%s", proto, item.Alias, hostname)
			} else {
				item.URL = fmt.Sprintf("%s://%s", proto, item.Alias)
			}
		}

		// prepend protocol if not exists
		if !strings.HasPrefix(item.URL, "http://") && !strings.HasPrefix(item.URL, "https://") {
			item.URL = fmt.Sprintf("%s://%s", proto, item.URL)
		}

		hp.Add(&item)
	}

	ret := hp.Values()
	// sort items in each category
	for _, category := range ret {
		category.Sort(request.SortMethod)
	}
	// sort categories
	overrides := homepage.GetOverrideConfig()
	slices.SortStableFunc(ret, func(a, b *homepage.Category) int {
		// if category is "Hidden", move it to the end of the list
		if a.Name == homepage.CategoryHidden {
			return 1
		}
		if b.Name == homepage.CategoryHidden {
			return -1
		}
		// sort categories by order in config
		return overrides.CategoryOrder[a.Name] - overrides.CategoryOrder[b.Name]
	})
	return ret
}
