package engine import ( "context" "fmt" "strings" "github.com/insertr/insertr/internal/db" "golang.org/x/net/html" ) // AuthProvider represents authentication provider information type AuthProvider struct { Type string // "mock", "jwt", "authentik" } // ContentEngine is the unified content processing engine type ContentEngine struct { idGenerator *IDGenerator client db.ContentRepository authProvider *AuthProvider injector *Injector } // NewContentEngine creates a new content processing engine func NewContentEngine(client db.ContentRepository) *ContentEngine { authProvider := &AuthProvider{Type: "mock"} // default return &ContentEngine{ idGenerator: NewIDGenerator(), client: client, authProvider: authProvider, injector: NewInjector(client, "", nil), // siteID will be set per operation } } // NewContentEngineWithAuth creates a new content processing engine with auth config func NewContentEngineWithAuth(client db.ContentRepository, authProvider *AuthProvider) *ContentEngine { if authProvider == nil { authProvider = &AuthProvider{Type: "mock"} } return &ContentEngine{ idGenerator: NewIDGenerator(), client: client, authProvider: authProvider, injector: NewInjectorWithAuth(client, "", authProvider, nil), // siteID will be set per operation } } // ProcessContent processes HTML content according to the specified mode func (e *ContentEngine) ProcessContent(input ContentInput) (*ContentResult, error) { // 1. Parse HTML doc, err := html.Parse(strings.NewReader(string(input.HTML))) if err != nil { return nil, fmt.Errorf("parsing HTML: %w", err) } // 2. Find insertr and collection elements insertrElements, collectionElements := e.findEditableElements(doc) // 3. Process regular .insertr elements generatedIDs := make(map[string]string) processedElements := make([]ProcessedElement, len(insertrElements)) for i, elem := range insertrElements { // Generate structural ID (always deterministic) id := e.idGenerator.Generate(elem.Node, input.FilePath) // Database-first approach: Check if content already exists existingContent, err := e.client.GetContent(context.Background(), input.SiteID, id) contentExists := (err == nil && existingContent != nil) generatedIDs[fmt.Sprintf("element_%d", i)] = id processedElements[i] = ProcessedElement{ Node: elem.Node, ID: id, Generated: !contentExists, // Mark as generated only if new to database Tag: elem.Node.Data, Classes: GetClasses(elem.Node), } // Add/update content attributes to the node (only content-id now) e.addContentAttributes(elem.Node, id) // Store content only for truly new elements (database-first check) if !contentExists && (input.Mode == Enhancement || input.Mode == ContentInjection) { // Extract content and template from the unprocessed element htmlContent := e.extractHTMLContent(elem.Node) originalTemplate := e.extractOriginalTemplate(elem.Node) // Store in database via content client _, err := e.client.CreateContent(context.Background(), input.SiteID, id, htmlContent, originalTemplate, "system") if err != nil { // Log error but don't fail the enhancement - content just won't be stored fmt.Printf("⚠️ Failed to store content for %s: %v\n", id, err) } else { fmt.Printf("✅ Created new content: %s (html)\n", id) } } } // 4. Process .insertr-add collection elements for _, collectionElem := range collectionElements { // Generate structural ID for the collection container collectionID := e.idGenerator.Generate(collectionElem.Node, input.FilePath) // Add data-collection-id attribute to the collection container SetAttribute(collectionElem.Node, "data-collection-id", collectionID) // Process collection during enhancement or content injection if input.Mode == Enhancement || input.Mode == ContentInjection { err := e.processCollection(collectionElem.Node, collectionID, input.SiteID) if err != nil { fmt.Printf("⚠️ Failed to process collection %s: %v\n", collectionID, err) } else { fmt.Printf("✅ Processed collection: %s\n", collectionID) } } } // 6. Inject content if required by mode if input.Mode == Enhancement || input.Mode == ContentInjection { err = e.injectContent(processedElements, input.SiteID) if err != nil { return nil, fmt.Errorf("injecting content: %w", err) } } // TODO: Implement collection-specific content injection here if needed // 6. Inject editor assets for enhancement mode (development) if input.Mode == Enhancement { injector := NewInjectorWithAuth(e.client, input.SiteID, e.authProvider, nil) injector.InjectEditorAssets(doc, true, "") } return &ContentResult{ Document: doc, Elements: processedElements, GeneratedIDs: generatedIDs, }, nil }