package handlers

import (
	"errors"
	"net/http"
	"strings"

	"github.com/labstack/echo/v4"
	"gorm.io/gorm"

	appmiddleware "git.ma-al.com/goc_marek/ps_shop/internal/http/middleware"
	pscart "git.ma-al.com/goc_marek/ps_shop/internal/prestashop/cart"
	pscatalog "git.ma-al.com/goc_marek/ps_shop/internal/prestashop/catalog"
	psconfig "git.ma-al.com/goc_marek/ps_shop/internal/prestashop/config"
	pscustomer "git.ma-al.com/goc_marek/ps_shop/internal/prestashop/customer"
	psroutes "git.ma-al.com/goc_marek/ps_shop/internal/prestashop/routes"
	"git.ma-al.com/goc_marek/ps_shop/internal/render"
	"git.ma-al.com/goc_marek/ps_shop/internal/viewmodel"
)

const categorySlugContextKey = "category_slug"
const categoryIDContextKey = "category_id"

type CategoryHandler struct {
	catalog    *pscatalog.Service
	customers  *pscustomer.Service
	carts      *pscart.Service
	renderer   *render.Engine
	config     psconfig.Config
	products   *psroutes.ProductRoute
	categories *psroutes.CategoryRoute
}

func NewCategoryHandler(catalog *pscatalog.Service, customers *pscustomer.Service, carts *pscart.Service, renderer *render.Engine, cfg psconfig.Config, products *psroutes.ProductRoute, categories *psroutes.CategoryRoute) *CategoryHandler {
	return &CategoryHandler{
		catalog:    catalog,
		customers:  customers,
		carts:      carts,
		renderer:   renderer,
		config:     cfg,
		products:   products,
		categories: categories,
	}
}

func (h *CategoryHandler) Show(c echo.Context) error {
	session := appmiddleware.GetSession(c)
	if h == nil || h.catalog == nil || h.renderer == nil {
		return echo.NewHTTPError(http.StatusInternalServerError, "category handler is not initialized")
	}

	languageID := int64Default(session.LanguageID, 1)
	languageID = h.catalog.ResolveLanguageID(c.Request().Context(), c.Request(), languageID)
	shopID := int64Default(session.ShopID, 1)

	category, err := h.catalog.GetCategoryPage(c.Request().Context(), pscatalog.CategoryPageRequest{
		ID:         categoryID(c),
		Slug:       categorySlug(c),
		LanguageID: languageID,
		ShopID:     shopID,
	})
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return echo.NewHTTPError(http.StatusNotFound, "category not found")
		}
		return echo.NewHTTPError(http.StatusInternalServerError, "category query failed: "+err.Error())
	}

	var profile *pscustomer.Profile
	if session.CustomerID != nil && h.customers != nil {
		profile, err = h.customers.GetByID(c.Request().Context(), *session.CustomerID)
		if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
			return echo.NewHTTPError(http.StatusInternalServerError, "customer query failed: "+err.Error())
		}
	}

	var cartSummary *pscart.Summary
	if session.CartID != nil && h.carts != nil {
		cartSummary, err = h.carts.SummaryByID(c.Request().Context(), *session.CartID)
		if err != nil {
			return echo.NewHTTPError(http.StatusInternalServerError, "cart query failed: "+err.Error())
		}
	}

	page := viewmodel.CategoryPageData{
		Category:    *category,
		Session:     session,
		Customer:    profile,
		CartSummary: cartSummary,
		ShopBaseURL: h.config.PrestaShopBaseURL,
	}
	assignCategoryProductLinks(c.Request(), h.products, &page)
	menu, err := loadMenu(c.Request(), h.catalog, h.categories, languageID, shopID)
	if err != nil {
		return echo.NewHTTPError(http.StatusInternalServerError, "menu query failed: "+err.Error())
	}
	page.Menu = menu
	locale, err := loadHeaderLocale(c.Request(), h.catalog, session, languageID)
	if err != nil {
		return echo.NewHTTPError(http.StatusInternalServerError, "locale query failed: "+err.Error())
	}
	page.Locale = locale

	if err := h.renderer.Category(c.Response(), c.Request(), page); err != nil {
		return echo.NewHTTPError(http.StatusInternalServerError, "category render failed: "+err.Error())
	}
	return nil
}

func SetCategorySlug(c echo.Context, slug string) {
	c.Set(categorySlugContextKey, slug)
}

func SetCategoryID(c echo.Context, id int64) {
	c.Set(categoryIDContextKey, id)
}

func categorySlug(c echo.Context) string {
	if value := c.Get(categorySlugContextKey); value != nil {
		if slug, ok := value.(string); ok && slug != "" {
			return slug
		}
	}
	return strings.TrimSpace(c.Param("slug"))
}

func categoryID(c echo.Context) int64 {
	if value := c.Get(categoryIDContextKey); value != nil {
		if id, ok := value.(int64); ok {
			return id
		}
	}
	return 0
}

func assignCategoryProductLinks(req *http.Request, route *psroutes.ProductRoute, page *viewmodel.CategoryPageData) {
	if page == nil {
		return
	}
	langPrefix := requestLanguagePrefix(req)
	categoryPath := page.Category.Slug
	for i := range page.Category.Products {
		product := &page.Category.Products[i]
		product.URL = route.BuildPath(psroutes.ProductURLData{
			ID:             product.ID,
			Slug:           product.Slug,
			CategoryPath:   categoryPath,
			EAN13:          product.EAN13,
			LanguagePrefix: langPrefix,
		})
	}
}
