package catalog import ( "context" "fmt" "github.com/descrybe/descrybe-v2/apps/api/internal/billing" "github.com/google/uuid" "github.com/jackc/pgx/v5/pgxpool" ) // EnsureCategoryAttributeLinks removes orphan category_attributes rows (missing // category or attribute) without wiping taxonomy or valid links. Returns orphans // removed and remaining link count. func EnsureCategoryAttributeLinks(ctx context.Context, pool *pgxpool.Pool, companyID uuid.UUID) (orphansRemoved int, links int, err error) { if pool == nil { return 0, 0, fmt.Errorf("nil pool") } ct, err := pool.Exec(ctx, ` DELETE FROM category_attributes ca WHERE ca.company_id = $1 AND ( NOT EXISTS ( SELECT 1 FROM categories c WHERE c.company_id = ca.company_id AND c.unique_id = ca.category_unique_id ) OR NOT EXISTS ( SELECT 1 FROM attributes a WHERE a.id = ca.attribute_id AND a.company_id = ca.company_id ) )`, companyID) if err != nil { return 0, 0, fmt.Errorf("purge orphan category_attributes: %w", err) } orphansRemoved = int(ct.RowsAffected()) if err := pool.QueryRow(ctx, ` SELECT count(*)::int FROM category_attributes WHERE company_id = $1`, companyID).Scan(&links); err != nil { return orphansRemoved, 0, err } return orphansRemoved, links, nil } // RepairCompanyCategoryEnhancePrompts is the company-scoped variant of // RepairA1DemoCategoryEnhancePrompts: same repairedCategoryEnhancePromptMap // (sl + "*" with CategoryEnhanceUserTemplate / {{attrs}}), applied to one company. // Idempotent — already-OK categories count as already_ok, not updated. func RepairCompanyCategoryEnhancePrompts(ctx context.Context, pool *pgxpool.Pool, companyID uuid.UUID) (updated, alreadyOK, emptySkipped int, err error) { if pool == nil { return 0, 0, 0, fmt.Errorf("nil pool") } want, err := repairedCategoryEnhancePromptMap() if err != nil { return 0, 0, 0, err } summary := &RepairCategoryEnhancePromptsResult{ByCompany: map[string]int{}} if _, err := repairCompanyCategoryEnhancePrompts(ctx, pool, companyID, companyID.String(), want, false, summary); err != nil { return 0, 0, 0, err } return summary.Updated, summary.AlreadyOK, summary.EmptySkipped, nil } // ResolvePlatformDemoCompanyID finds the Platform Demo sandbox (never A1 cohort). func ResolvePlatformDemoCompanyID(ctx context.Context, pool *pgxpool.Pool) (uuid.UUID, error) { var id uuid.UUID err := pool.QueryRow(ctx, ` SELECT c.id FROM companies c WHERE c.name = $1 AND COALESCE(c.legacy_company_id, '') <> $2 ORDER BY c.created_at ASC LIMIT 1`, platformDemoCompanyName, billing.A1LegacyCompanyID).Scan(&id) if err != nil { return uuid.Nil, fmt.Errorf("platform demo company not found: %w", err) } return id, nil }