package processing import ( "context" "fmt" "github.com/descrybe/descrybe-v2/apps/api/internal/aiprompts" "github.com/descrybe/descrybe-v2/apps/api/internal/catalog" "github.com/google/uuid" "github.com/jackc/pgx/v5/pgxpool" ) // FixCompanyCatalogOpts controls optional Fix A1 / catalog hygiene steps. type FixCompanyCatalogOpts struct { // BackfillCategories copies mapped_data category unique_ids onto empty // processed_products.category. BackfillCategories bool // ReprocessSampleLimit caps recommended raw_product_ids in the response // (0 = counts only, no sample ids). ReprocessSampleLimit int // AIPrompts optionally re-applies BuiltInDefaults product_enhance per content language. AIPrompts *aiprompts.Service } // FixCompanyCatalogResult is the idempotent admin Fix A1 report. type FixCompanyCatalogResult struct { CompanyID uuid.UUID `json:"company_id"` CompanyName string `json:"company_name"` A1Cohort bool `json:"a1_cohort"` CategoryAttributeOrphansRemoved int `json:"category_attribute_orphans_removed"` CategoryAttributeLinks int `json:"category_attribute_links"` CategoryPromptsUpdated int `json:"category_prompts_updated"` CategoryPromptsAlreadyOK int `json:"category_prompts_already_ok"` CategoryPromptsEmpty int `json:"category_prompts_empty_skipped"` // Prompts aliases category_prompts_updated for flash.admin.fixA1Success {prompts}. Prompts int `json:"prompts"` ProductEnhanceLanguages int `json:"product_enhance_languages"` WeakHashesCleared int `json:"weak_hashes_cleared"` CategoriesBackfilled int `json:"categories_backfilled"` // Hashes / Categories alias the long names for flash.admin.fixA1Success. Hashes int `json:"hashes"` Categories int `json:"categories"` DescriptionsNormalized int `json:"descriptions_normalized"` DescriptionsBackfilled int `json:"descriptions_backfilled"` MappedDescriptionsFlat int `json:"mapped_descriptions_flattened"` MetaBackfilled int `json:"meta_backfilled"` AttributesSanitized int64 `json:"attributes_sanitized"` ProductsScanned int `json:"products_scanned"` ReprocessNeededCount int `json:"reprocess_needed_count"` ReprocessSampleRawProductIDs []uuid.UUID `json:"reprocess_sample_raw_product_ids,omitempty"` } // FixCompanyCatalog runs the admin Fix A1 / catalog hygiene sequence in place. // Never clears the catalog or uses A1 as a clone destination. // Step 2 applies RepairCompanyCategoryEnhancePrompts (same repaired map as // RepairA1DemoCategoryEnhancePrompts / cmd/repair-category-prompts -apply). // Legacy converters + weak-hash clear live in FixCatalogHygieneWithIDs. func FixCompanyCatalog(ctx context.Context, pool *pgxpool.Pool, companyID uuid.UUID, companyName string, a1Cohort bool, opts FixCompanyCatalogOpts) (FixCompanyCatalogResult, error) { out := FixCompanyCatalogResult{ CompanyID: companyID, CompanyName: companyName, A1Cohort: a1Cohort, } if pool == nil { return out, fmt.Errorf("nil pool") } orphans, links, err := catalog.EnsureCategoryAttributeLinks(ctx, pool, companyID) if err != nil { return out, err } out.CategoryAttributeOrphansRemoved = orphans out.CategoryAttributeLinks = links updated, alreadyOK, emptySkipped, err := catalog.RepairCompanyCategoryEnhancePrompts(ctx, pool, companyID) if err != nil { return out, err } out.CategoryPromptsUpdated = updated out.CategoryPromptsAlreadyOK = alreadyOK out.CategoryPromptsEmpty = emptySkipped if opts.AIPrompts != nil { n, err := opts.AIPrompts.ApplyBuiltInProductEnhance(ctx, companyID) if err != nil { return out, fmt.Errorf("product_enhance templates: %w", err) } out.ProductEnhanceLanguages = n } hygiene, clearedIDs, err := FixCatalogHygieneWithIDs(ctx, pool, companyID, opts.BackfillCategories) if err != nil { return out, err } out.WeakHashesCleared = hygiene.WeakHashesCleared out.CategoriesBackfilled = hygiene.CategoriesBackfilled out.DescriptionsNormalized = hygiene.DescriptionsNormalized out.DescriptionsBackfilled = hygiene.DescriptionsBackfilled out.MappedDescriptionsFlat = hygiene.MappedDescriptionsFlat out.MetaBackfilled = hygiene.MetaBackfilled out.ProductsScanned = hygiene.ProductsScanned sanitized, err := BackfillCompanyProcessedAttributes(ctx, pool, companyID) if err != nil { return out, fmt.Errorf("sanitize attributes: %w", err) } out.AttributesSanitized = sanitized needed, sample, err := RecommendReprocessRawProductIDs(ctx, pool, companyID, clearedIDs, opts.ReprocessSampleLimit) if err != nil { return out, err } out.ReprocessNeededCount = needed out.ReprocessSampleRawProductIDs = sample // Short keys for flash.admin.fixA1Success {prompts,hashes,categories}. out.Prompts = out.CategoryPromptsUpdated out.Hashes = out.WeakHashesCleared out.Categories = out.CategoriesBackfilled return out, nil }