package v1

import (
	"context"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/yusing/godoxy/internal/homepage"
	"github.com/yusing/godoxy/internal/route/routes"
	apitypes "github.com/yusing/goutils/apitypes"

	_ "unsafe"
)

type GetFavIconRequest struct {
	URL     string               `form:"url" binding:"required_without=Alias"`
	Alias   string               `form:"alias" binding:"required_without=URL"`
	Variant homepage.IconVariant `form:"variant" binding:"omitempty,oneof=light dark"`
} //	@name	GetFavIconRequest

// @x-id				"favicon"
// @BasePath		/api/v1
// @Summary		Get favicon
// @Description	Get favicon
// @Tags			v1
// @Accept			json
// @Produce		image/svg+xml,image/x-icon,image/png,image/webp
// @Param			url		query		string	false	"URL of the route"
// @Param			alias	query		string	false	"Alias of the route"
// @Success		200		{array}		homepage.FetchResult
// @Failure		400		{object}	apitypes.ErrorResponse "Bad Request: alias is empty or route is not HTTPRoute"
// @Failure		403		{object}	apitypes.ErrorResponse "Forbidden: unauthorized"
// @Failure		404		{object}	apitypes.ErrorResponse "Not Found: route or icon not found"
// @Failure		500		{object}	apitypes.ErrorResponse "Internal Server Error: internal error"
// @Router			/favicon [get]
func FavIcon(c *gin.Context) {
	var request GetFavIconRequest
	if err := c.ShouldBindQuery(&request); err != nil {
		c.JSON(http.StatusBadRequest, apitypes.Error("invalid request", err))
		return
	}

	// try with url
	if request.URL != "" {
		var iconURL homepage.IconURL
		if err := iconURL.Parse(request.URL); err != nil {
			c.JSON(http.StatusBadRequest, apitypes.Error("invalid url", err))
			return
		}
		icon := &iconURL
		if request.Variant != homepage.IconVariantNone {
			icon = icon.WithVariant(request.Variant)
		}
		fetchResult, err := homepage.FetchFavIconFromURL(c.Request.Context(), icon)
		if err != nil {
			homepage.GinFetchError(c, fetchResult.StatusCode, err)
			return
		}
		c.Data(fetchResult.StatusCode, fetchResult.ContentType(), fetchResult.Icon)
		return
	}

	// try with alias
	result, err := GetFavIconFromAlias(c.Request.Context(), request.Alias, request.Variant)
	if err != nil {
		homepage.GinFetchError(c, result.StatusCode, err)
		return
	}
	c.Data(result.StatusCode, result.ContentType(), result.Icon)
}

//go:linkname GetFavIconFromAlias v1.GetFavIconFromAlias
func GetFavIconFromAlias(ctx context.Context, alias string, variant homepage.IconVariant) (homepage.FetchResult, error) {
	// try with route.Icon
	r, ok := routes.HTTP.Get(alias)
	if !ok {
		return homepage.FetchResultWithErrorf(http.StatusNotFound, "route not found")
	}

	var (
		result homepage.FetchResult
		err    error
	)
	hp := r.HomepageItem()
	if hp.Icon != nil {
		if hp.Icon.IconSource == homepage.IconSourceRelative {
			result, err = homepage.FindIcon(ctx, r, *hp.Icon.FullURL, variant)
		} else if variant != homepage.IconVariantNone {
			result, err = homepage.FetchFavIconFromURL(ctx, hp.Icon.WithVariant(variant))
			if err != nil {
				// fallback to no variant
				result, err = homepage.FetchFavIconFromURL(ctx, hp.Icon.WithVariant(homepage.IconVariantNone))
			}
		} else {
			result, err = homepage.FetchFavIconFromURL(ctx, hp.Icon)
		}
	} else {
		// try extract from "link[rel=icon]"
		result, err = homepage.FindIcon(ctx, r, "/", variant)
	}
	if result.StatusCode == 0 {
		result.StatusCode = http.StatusOK
	}
	return result, err
}
