package usecase

import (
	"context"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"sort"
	"strings"

	"apps/backend/internal/module/radar/domain"
)

func (s *Service) GetDemandMap(ctx context.Context, ownerUID int64, productID string) (*domain.DemandMap, error) {
	if ownerUID <= 0 || strings.TrimSpace(productID) == "" {
		return nil, fmt.Errorf("%w: owner and product are required", domain.ErrValidation)
	}
	value, err := s.Repo.GetDemandMap(ctx, ownerUID, productID)
	if err == nil {
		return value, nil
	}
	if err != domain.ErrNotFound {
		return nil, err
	}
	if s.ProductSource == nil {
		return nil, fmt.Errorf("%w: product catalog unavailable", domain.ErrNotReady)
	}
	product, err := s.ProductSource.GetProduct(ctx, ownerUID, productID)
	if err != nil {
		return nil, err
	}
	if product == nil {
		return nil, domain.ErrNotFound
	}
	value = baselineDemandMap(product)
	return s.Repo.SaveDemandMap(ctx, value, 0)
}

func (s *Service) UpdateDemandMap(ctx context.Context, ownerUID int64, value *domain.DemandMap, expectedVersion int64) (*domain.DemandMap, error) {
	if value == nil || ownerUID <= 0 || strings.TrimSpace(value.ProductID) == "" {
		return nil, fmt.Errorf("%w: owner and product are required", domain.ErrValidation)
	}
	if value.OwnerUID != 0 && value.OwnerUID != ownerUID {
		return nil, domain.ErrForbidden
	}
	value.OwnerUID = ownerUID
	if expectedVersion < 1 {
		return nil, fmt.Errorf("%w: expected map version required", domain.ErrValidation)
	}
	current, err := s.GetDemandMap(ctx, ownerUID, value.ProductID)
	if err != nil {
		return nil, err
	}
	value.DemandInputVersion = current.DemandInputVersion
	value.SourceBasis = append([]string(nil), current.SourceBasis...)
	if value.AIEnrichedAt == 0 {
		value.AIEnrichedAt = current.AIEnrichedAt
	}
	value.MapVersion = current.MapVersion
	value.UpdatedAt = domain.NowNano()
	return s.Repo.SaveDemandMap(ctx, value, expectedVersion)
}

func baselineDemandMap(product *ProductCatalogProduct) *domain.DemandMap {
	mapPhrase := func(text, kind, basisKind, basisText string) domain.DemandMapPhrase {
		return domain.DemandMapPhrase{Text: strings.TrimSpace(text), Kind: kind, BasisKind: basisKind, BasisText: basisText, Origin: "product", Enabled: strings.TrimSpace(text) != ""}
	}
	phrases := func(items []string, kind, basisKind string, include bool) []domain.DemandMapPhrase {
		out := make([]domain.DemandMapPhrase, 0, len(items))
		for _, item := range items {
			raw := strings.TrimSpace(item)
			if raw == "" {
				continue
			}
			if !include {
				out = append(out, mapPhrase(raw, kind, basisKind, product.Label))
				continue
			}
			variants := domain.SearchableTermVariants(raw, true)
			if len(variants) > domain.MaxSearchableVariantsPerTerm {
				variants = variants[:domain.MaxSearchableVariantsPerTerm]
			}
			for _, term := range variants {
				out = append(out, mapPhrase(term, kind, basisKind, raw))
			}
		}
		return out
	}
	pain := phrases(product.PainPoints, "pain", "pain_point", true)
	scenario := phrases([]string{product.ProductContext}, "scenario", "product_context", true)
	outcomes := phrases(product.MatchTags, "outcome", "match_tag", true)
	solution := phrases(product.ProviderCapabilityTerms, "solution", "provider_capability", true)
	exclusions := phrases(product.ProviderExcludeTerms, "exclusion", "provider_exclude", false)
	state := "incomplete"
	if len(pain) > 0 && len(scenario) > 0 && len(solution) > 0 {
		state = "ready"
	}
	version := "demand-" + DemandInputFingerprint(product)
	return &domain.DemandMap{
		ID: "", OwnerUID: product.OwnerUID, ProductID: product.ID,
		DemandInputVersion: version, MapVersion: 1, State: state,
		PainPhrases: pain, ScenarioPhrases: scenario, DesiredOutcomes: outcomes,
		SolutionSignals: solution, ExclusionSignals: exclusions,
		SourceBasis: []string{"product:" + product.ID}, CustomPhrases: []domain.DemandMapPhrase{},
		UpdatedAt: domain.NowNano(),
	}
}

// DemandInputFingerprint intentionally excludes display-only product fields
// (name, image, price). A wording edit that changes what the provider solves
// must create a new input version; a catalog rename must not invalidate a
// user's reviewed query plan.
func DemandInputFingerprint(product *ProductCatalogProduct) string {
	if product == nil {
		return "unknown"
	}
	normalize := func(values []string) []string {
		out := make([]string, 0, len(values))
		for _, value := range values {
			value = strings.ToLower(strings.Join(strings.Fields(strings.TrimSpace(value)), " "))
			if value != "" {
				out = append(out, value)
			}
		}
		sort.Strings(out)
		return out
	}
	parts := []string{
		strings.TrimSpace(product.BrandID),
		strings.ToLower(strings.Join(strings.Fields(strings.TrimSpace(product.ProductContext)), " ")),
		strings.Join(normalize(product.PainPoints), "\x1f"),
		strings.Join(normalize(product.MatchTags), "\x1f"),
		strings.Join(normalize(product.ProviderCapabilityTerms), "\x1f"),
		strings.Join(normalize(product.ProviderExcludeTerms), "\x1f"),
	}
	sum := sha256.Sum256([]byte(strings.Join(parts, "\x1e")))
	return hex.EncodeToString(sum[:])[:16]
}
