package usecase

import (
	"context"
	"strings"

	app "haixun-backend/internal/library/errors"
	"haixun-backend/internal/library/errors/code"
	"haixun-backend/internal/library/placement"
	"haixun-backend/internal/model/brand/domain/entity"
	domusecase "haixun-backend/internal/model/brand/domain/usecase"

	"github.com/google/uuid"
)

func (u *brandUseCase) ListProducts(ctx context.Context, tenantID, ownerUID, brandID string) (*domusecase.ListProductsResult, error) {
	item, err := u.assertOwned(ctx, tenantID, ownerUID, brandID)
	if err != nil {
		return nil, err
	}
	list := make([]domusecase.ProductSummary, 0, len(item.Products))
	for _, product := range item.Products {
		list = append(list, toProductSummary(product))
	}
	return &domusecase.ListProductsResult{List: list}, nil
}

func (u *brandUseCase) CreateProduct(ctx context.Context, req domusecase.CreateProductRequest) (*domusecase.ProductSummary, error) {
	if _, err := u.assertOwned(ctx, req.TenantID, req.OwnerUID, req.BrandID); err != nil {
		return nil, err
	}
	label := strings.TrimSpace(req.Label)
	contextRaw := strings.TrimSpace(req.ProductContext)
	if label == "" {
		return nil, app.For(code.Brand).InputMissingRequired("product label is required")
	}
	if !placement.HasProductContext(contextRaw) {
		return nil, app.For(code.Brand).InputMissingRequired("product context is required")
	}
	product := entity.Product{
		ID:             uuid.NewString(),
		Label:          label,
		ProductContext: contextRaw,
		MatchTags:      normalizeMatchTags(req.MatchTags),
	}
	created, err := u.repo.PushProduct(ctx, req.TenantID, req.OwnerUID, req.BrandID, product)
	if err != nil {
		return nil, err
	}
	summary := toProductSummary(*created)
	return &summary, nil
}

func (u *brandUseCase) UpdateProduct(ctx context.Context, req domusecase.UpdateProductRequest) (*domusecase.ProductSummary, error) {
	brand, err := u.assertOwned(ctx, req.TenantID, req.OwnerUID, req.BrandID)
	if err != nil {
		return nil, err
	}
	if placement.FindProduct(*brand, req.ProductID) == nil {
		return nil, app.For(code.Brand).ResNotFound("product not found")
	}
	patch := map[string]interface{}{}
	if req.Label != nil {
		label := strings.TrimSpace(*req.Label)
		if label == "" {
			return nil, app.For(code.Brand).InputMissingRequired("product label is required")
		}
		patch["products.$.label"] = label
	}
	if req.ProductContext != nil {
		contextRaw := strings.TrimSpace(*req.ProductContext)
		if !placement.HasProductContext(contextRaw) {
			return nil, app.For(code.Brand).InputMissingRequired("product context is required")
		}
		patch["products.$.product_context"] = contextRaw
	}
	if req.MatchTagsSet {
		patch["products.$.match_tags"] = normalizeMatchTags(req.MatchTags)
	}
	if len(patch) == 0 {
		return nil, app.For(code.Brand).InputMissingRequired("product patch is empty")
	}
	updated, err := u.repo.UpdateProduct(ctx, req.TenantID, req.OwnerUID, req.BrandID, req.ProductID, patch)
	if err != nil {
		return nil, err
	}
	if brand.ProductID == req.ProductID {
		_ = u.syncProductSnapshot(ctx, req.TenantID, req.OwnerUID, req.BrandID, brand.ProductID)
	}
	summary := toProductSummary(*updated)
	return &summary, nil
}

func (u *brandUseCase) DeleteProduct(ctx context.Context, tenantID, ownerUID, brandID, productID string) error {
	brand, err := u.assertOwned(ctx, tenantID, ownerUID, brandID)
	if err != nil {
		return err
	}
	if placement.FindProduct(*brand, productID) == nil {
		return app.For(code.Brand).ResNotFound("product not found")
	}
	if err := u.repo.PullProduct(ctx, tenantID, ownerUID, brandID, productID); err != nil {
		return err
	}
	if brand.ProductID == productID {
		_, _ = u.repo.Update(ctx, tenantID, ownerUID, brandID, map[string]interface{}{
			"product_id":      "",
			"product_context": "",
		})
	}
	return nil
}

func (u *brandUseCase) syncProductSnapshot(ctx context.Context, tenantID, ownerUID, brandID, productID string) error {
	brand, err := u.assertOwned(ctx, tenantID, ownerUID, brandID)
	if err != nil {
		return err
	}
	snapshot := placement.ResolveBrandProductContext(*brand, productID, "")
	patch := map[string]interface{}{
		"product_context": snapshot,
	}
	if id := strings.TrimSpace(productID); id != "" {
		patch["product_id"] = id
	} else {
		patch["product_id"] = ""
	}
	_, err = u.repo.Update(ctx, tenantID, ownerUID, brandID, patch)
	return err
}

func toProductSummary(item entity.Product) domusecase.ProductSummary {
	return domusecase.ProductSummary{
		ID:             item.ID,
		Label:          item.Label,
		ProductContext: item.ProductContext,
		MatchTags:      append([]string(nil), item.MatchTags...),
		CreateAt:       item.CreateAt,
		UpdateAt:       item.UpdateAt,
	}
}

func normalizeMatchTags(tags []string) []string {
	if len(tags) == 0 {
		return nil
	}
	seen := map[string]struct{}{}
	out := make([]string, 0, len(tags))
	for _, raw := range tags {
		tag := strings.TrimSpace(raw)
		if tag == "" {
			continue
		}
		key := strings.ToLower(tag)
		if _, ok := seen[key]; ok {
			continue
		}
		seen[key] = struct{}{}
		out = append(out, tag)
	}
	return out
}
