package productDescriptionRepo

import (
	"fmt"

	"git.ma-al.com/goc_daniel/b2b/app/db"
	"git.ma-al.com/goc_daniel/b2b/app/model"
	"git.ma-al.com/goc_daniel/b2b/app/model/dbmodel"
	constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data"
	"github.com/WinterYukky/gorm-extra-clause-plugin/exclause"
)

type UIProductDescriptionRepo interface {
	GetProductDescription(productID uint, productid_lang uint) (*model.ProductDescription, error)
	CreateIfDoesNotExist(productID uint, productid_lang uint) error
	UpdateFields(productID uint, productid_lang uint, updates map[string]string) error
	GetMeiliProducts(id_lang uint, offset, limit int) ([]model.MeiliSearchProduct, error)
}

type ProductDescriptionRepo struct{}

func New() UIProductDescriptionRepo {
	return &ProductDescriptionRepo{}
}

// We assume that any user has access to all product descriptions
func (r *ProductDescriptionRepo) GetProductDescription(productID uint, productid_lang uint) (*model.ProductDescription, error) {
	var ProductDescription model.ProductDescription

	err := db.Get().
		Where(&dbmodel.PsProductLang{
			IDProduct: int32(productID),
			IDShop:    int32(constdata.SHOP_ID),
			IDLang:    int32(productid_lang),
		}).
		First(&ProductDescription).Error
	if err != nil {
		return nil, fmt.Errorf("database error: %w", err)
	}

	return &ProductDescription, nil
}

// If it doesn't exist, returns an error.
func (r *ProductDescriptionRepo) CreateIfDoesNotExist(productID uint, productid_lang uint) error {
	record := model.ProductDescription{
		ProductID: productID,
		ShopID:    constdata.SHOP_ID,
		LangID:    productid_lang,
	}

	err := db.Get().
		Where(&dbmodel.PsProductLang{
			IDProduct: int32(productID),
			IDShop:    int32(constdata.SHOP_ID),
			IDLang:    int32(productid_lang),
		}).
		FirstOrCreate(&record).Error
	if err != nil {
		return fmt.Errorf("database error: %w", err)
	}

	return nil
}

func (r *ProductDescriptionRepo) UpdateFields(productID uint, productid_lang uint, updates map[string]string) error {
	if len(updates) == 0 {
		return nil
	}
	updatesIface := make(map[string]interface{}, len(updates))
	for k, v := range updates {
		updatesIface[k] = v
	}

	err := db.Get().
		Model(&dbmodel.PsProductLang{}).
		Where(&dbmodel.PsProductLang{
			IDProduct: int32(productID),
			IDShop:    int32(constdata.SHOP_ID),
			IDLang:    int32(productid_lang),
		}).
		Updates(updatesIface).Error
	if err != nil {
		return fmt.Errorf("database error: %w", err)
	}

	return nil
}

// GetMeiliProductsBatchedScanned returns a batch of products with LIMIT/OFFSET pagination
// The scanning is done inside the repo to keep the service layer cleaner
func (r *ProductDescriptionRepo) GetMeiliProducts(id_lang uint, offset, limit int) ([]model.MeiliSearchProduct, error) {

	var products []model.MeiliSearchProduct

	err := db.Get().
		Table("ps_product_shop ps").
		Select(`
        ps.id_product AS id_product,
        pl.name AS name,
        TRIM(REGEXP_REPLACE(REGEXP_REPLACE(pl.description_short, '<[^>]*>', ' '), '[[:space:]]+', ' ')) AS description,
        p.ean13,
        p.reference,
        ps.price,
        ps.id_category_default AS id_category,
        cl.name AS cat_name,
        cl.link_rewrite AS l_rew,
        COALESCE(vary.attributes, JSON_OBJECT()) AS attr,
        COALESCE(feat.features, JSON_OBJECT()) AS feat,
        img.id_image,
        cat.category_ids,
        (SELECT COUNT(*) FROM ps_product_attribute_shop pas2 WHERE pas2.id_product = ps.id_product AND pas2.id_shop = ?) AS variations
    `, constdata.SHOP_ID).
		Joins("JOIN ps_product p ON p.id_product = ps.id_product").
		Joins("JOIN ps_product_lang pl ON pl.id_product = ps.id_product AND pl.id_shop = ? AND pl.id_lang = ?", constdata.SHOP_ID, id_lang).
		Joins("JOIN ps_category_lang cl ON cl.id_category = ps.id_category_default AND cl.id_shop = ? AND cl.id_lang = ?", constdata.SHOP_ID, id_lang).
		Joins("LEFT JOIN variations vary ON vary.id_product = ps.id_product").
		Joins("LEFT JOIN features feat ON feat.id_product = ps.id_product").
		Joins("LEFT JOIN images img ON img.id_product = ps.id_product").
		Joins("LEFT JOIN categories cat ON cat.id_product = ps.id_product").
		Joins("JOIN products_page pp ON pp.id_product = ps.id_product").
		Where("ps.active = ?", 1).
		Order("ps.id_product").
		Clauses(exclause.With{CTEs: []exclause.CTE{
			{
				Name: "products_page",
				Subquery: exclause.Subquery{
					DB: db.Get().
						Model(&dbmodel.PsProductShop{}).
						Select("id_product, price").
						Where("id_shop = ? AND active = 1", constdata.SHOP_ID).
						Order("id_product").
						Limit(limit).
						Offset(offset),
				},
			},
			{
				Name: "variation_attributes",
				Subquery: exclause.Subquery{
					DB: db.Get().
						Table("ps_product_attribute_shop pas"). // <- explicit alias here
						Select(`
							pas.id_product,
							pag.id_attribute_group AS attribute_name,
							JSON_ARRAYAGG(DISTINCT pa.id_attribute) AS attribute_values
						`).
						Joins("JOIN ps_product_attribute_combination ppac ON ppac.id_product_attribute = pas.id_product_attribute").
						Joins("JOIN ps_attribute pa ON pa.id_attribute = ppac.id_attribute").
						Joins("JOIN ps_attribute_group pag ON pag.id_attribute_group = pa.id_attribute_group").
						Where("pas.id_shop = ?", constdata.SHOP_ID).
						Group("pas.id_product, pag.id_attribute_group"),
				},
			},
			{
				Name: "variations",
				Subquery: exclause.Subquery{
					DB: db.Get().
						Table("variation_attributes").
						Select("id_product, JSON_OBJECTAGG(attribute_name, attribute_values) AS attributes").
						Group("id_product"),
				},
			},
			{
				Name: "features",
				Subquery: exclause.Subquery{
					DB: db.Get().
						Table("ps_feature_product pfp"). // <- explicit alias
						Select("pfp.id_product, JSON_OBJECTAGG(pfp.id_feature, pfp.id_feature_value) AS features").
						Group("pfp.id_product"),
				},
			},
			{
				Name: "images",
				Subquery: exclause.Subquery{
					DB: db.Get().
						Model(&dbmodel.PsImageShop{}).
						Select("id_product, id_image").
						Where("id_shop = ? AND cover = 1", constdata.SHOP_ID),
				},
			},
			{
				Name: "categories",
				Subquery: exclause.Subquery{
					DB: db.Get().
						Model(&dbmodel.PsCategoryProduct{}).
						Select("id_product, JSON_ARRAYAGG(id_category) AS category_ids").
						Group("id_product"),
				},
			},
		}}).Find(&products).Error

	return products, err
}
